Using_apply_sapply_lapply_tapply_mapply_in_R

Reference: http://blog.fens.me/r-apply/

apply的家族函数

apply函数族是R语言中数据处理的一组核心函数,通过使用apply函数,我们可以实现对数据的循环、分组、过滤、类型控制等操作。但是,由于在R语言中apply函数与其他语言循环体的处理思路是完全不一样的,所以apply函数族一直是使用者玩不转一类核心函数。

很多R语言新手,写了很多的for循环代码,也不愿意多花点时间把apply函数的使用方法了解清楚,最后把R代码写的跟C似得,我严重鄙视只会写for的R程序员。

apply函数本身就是解决数据循环处理的问题,为了面向不同的数据类型,不同的返回值,apply函数组成了一个函数族,包括了8个功能类似的函数。这其中有些函数很相似,有些也不是太一样的。

Group 01-分组计算: tapply: input:vector, output:vector apply: input:list,data.frame,array, out:vector,martix

Group 02-多参数计算: mapply:input:vector(multi), output:vector,martrix

Group 03-循环迭代: lapply:input:list,data.frame,output:list Group 03.1-lapply的简化版 sapply -> input:list,data.frame,output:vector, martrix sapply 可设置返回值 -> input:list,data.frame,output:vector, martrix Group 03.2-lapply的递归版 rapply -> input:list,output:list

Group 04-环境空间遍历 eapply:input:environment,output:list

最常用的函数为apply和sapply

0. Data

We can simulate this data using rnorm, to create three sets of observations. The first has mean 0, second mean of 2, third of mean of 5, and with 30 rows.

# create data
set.seed(30)

# matrix
data <- cbind(rnorm(30, 0), 
              rnorm(30, 2), 
              rnorm(30, 5))
# matrix row and column
data <- matrix(data, nrow=30, ncol=3)

# check data
head(data, 5);tail(data, 5)
#            [,1]      [,2]     [,3]
# [1,] -1.2885182 0.2747975 5.974056
# [2,] -0.3476894 2.6148607 5.399185
# [3,] -0.5216288 2.7268751 5.348251
# [4,]  1.2734732 1.9578098 4.709509
# [5,]  1.8245206 2.2160018 6.576622
#             [,1]     [,2]     [,3]
# [26,] -1.0964020 1.084473 2.966420
# [27,] -0.5342207 4.598402 3.934047
# [28,] -1.4212030 1.488387 6.572125
# [29,] -1.2427383 1.133646 4.269551
# [30,]  0.2319362 2.387218 5.450516

1. apply

apply函数是最常用的代替for循环的函数。apply函数可以对矩阵、数据框、数组(二维、多维),按行或列进行循环计算,对子元素进行迭代,并把子元素以参数传递的形式给自定义的FUN函数中,并以返回计算结果。

1.1 apply使用方式

apply(X, MARGIN, FUN, ...) Apply Functions Over Array Margins, 对数组、矩阵、数据框的行1或者列2使用函数,按行或者按列进行计算。其中X为一个数组;

MARGIN为一个向量(表示要将函数FUN应用到X的行还是列),若为1表示取行,为2表示取列,为c(1,2)表示行、列都计算。

1.2 apply函数示例代码:

data_array <- matrix(1:20, ncol = 4);data_array
#      [,1] [,2] [,3] [,4]
# [1,]    1    6   11   16
# [2,]    2    7   12   17
# [3,]    3    8   13   18
# [4,]    4    9   14   19
# [5,]    5   10   15   20

apply(data_array, 1, mean)
# [1]  8.5  9.5 10.5 11.5 12.5

apply(data_array, 2, mean)
# [1]  3  8 13 18

ma <- matrix(c(1:4, 1, 6:8), nrow = 2);ma
#      [,1] [,2] [,3] [,4]
# [1,]    1    3    1    7
# [2,]    2    4    6    8

# c(1,2)表示行、列都计算
apply(ma, c(1,2), sum)
#      [,1] [,2] [,3] [,4]
# [1,]    1    3    1    7
# [2,]    2    4    6    8

# 2nd parameter 1表示row计算
apply(ma, 1, sum)
# [1] 12 20

# 2nd parameter 2表示column计算
apply(ma, 2, sum)
# [1]  3  7  7 15


x <- cbind(x1 = 3, x2 = c(4:1, 2:5));x
#      x1 x2
# [1,]  3  4
# [2,]  3  3
# [3,]  3  2
# [4,]  3  1
# [5,]  3  2
# [6,]  3  3
# [7,]  3  4
# [8,]  3  5
dimnames(x)[[1]] <- letters[1:8];x
#   x1 x2
# a  3  4
# b  3  3
# c  3  2
# d  3  1
# e  3  2
# f  3  3
# g  3  4
# h  3  5

apply(x, 2, mean, trim = .2) #截尾取按列平均值
#trim = .2是mean的参数
# x1 x2 
 # 3  3 

# calculate column sums
col.sums <- apply(x, 2, sum);col.sums
# x1 x2 
# 24 24 

# calculate row sums
row.sums <- apply(x, 1, sum);row.sums
# a b c d e f g h 
# 7 6 5 4 5 6 7 8 

# cbind row sums
cbind(x, Rtot = row.sums)
#   x1 x2 Rtot
# a  3  4    7
# b  3  3    6
# c  3  2    5
# d  3  1    4
# e  3  2    5
# f  3  3    6
# g  3  4    7
# h  3  5    8

# calculate column sums
c(col.sums, sum(col.sums))
# x1 x2    
# 24 24 48 

# row total sum and column total sum
rbind(cbind(x, Row_Total = row.sums), Column_Total = c(col.sums, sum(col.sums)))
#              x1 x2 Row_Total
# a             3  4         7
# b             3  3         6
# c             3  2         5
# d             3  1         4
# e             3  2         5
# f             3  3         6
# g             3  4         7
# h             3  5         8
# Column_Total 24 24        48

apply 2nd example

# Random Number Generation, reproduction
set.seed(10)

# created that matrix correctly, three columns each with a mean 0, 2 and 5 respectively. 
# We can use apply and the base mean function to check this.
Y1 <- rnorm(30, mean = 0, sd = 1)
Y2 <- rnorm(30, mean = 2, sd = 1)
Y3 <- rnorm(30, mean = 5, sd = 1)

# Create data frame
mdata <- data.frame(Y1, Y2, Y3);mdata

# as matrix and use apply to rows
mdata <- as.matrix(mdata)

# Passing a 1 in the second argument, we get 30 values back,
# giving the mean of each row. 
# Not the three numbers we were expecting, try again.
apply(mdata, 1, mean)
#  [1] 1.309137 2.093875 1.922304 2.308625 2.326993 2.390135 2.296567
#  [8] 1.465343 1.870509 1.615213 3.189751 1.854270 1.628219 2.867805
# [15] 1.761267 2.744413 1.599251 2.024418 2.065423 3.220264 2.198627
# [22] 1.419165 2.801994 2.578436 2.226954 2.523837 2.023332 2.960419
# [29] 1.689467 2.321084

# Passing a 2 in the second argument, we get 3 values back, Done Great. 
# We can see the mean of each column is roughly 0, 2, and 5 as we expected.
apply(mdata, 2, mean)
#         Y1         Y2         Y3 
# -0.3446764  1.8871329  4.9872532 


# create martix
# mdat <- matrix(c(1,2,3, 11,12,13), 
#                nrow = 2, 
#                ncol = 3, 
#                byrow = TRUE,
#                dimnames = list(c("row1", "row2"),
#                                c("C.1", "C.2", "C.3")))
# mdat

1.3 apply self defined functions

Let’s say I see that negative number and realise I wanted to only look at positive values.

Let’s see how many negative numbers each column has, using apply again:

# length and check negative number
length(mdata)
# [1] 90
length(mdata[mdata<0])
# [1] 20
apply(mdata, 2, function(x) length(x[x<0]))
# Y1 Y2 Y3 
# 20  0  0

So 20 negative values in column one, none negative value in column two, and none in column three.

More or less what we would expect for three normal distributions with the given means and sd of 1.

Here we have used a simple function we defined in the call to apply, rather than some built in function.

Note we did not specify a return value for our function. R will magically return the last evaluated value.

The actual function is using subsetting to extract all the elements in x that are less than 0, and then counting how many are left are using length.

The function takes one argument, which I have arbitrarily called x. In this case x will be a single column of the matrix. Is it a 1 column matrix or a just a vector? Let’s have a look:

# show code and output
apply(mdata, 2, function(x) is.matrix(x))
#    Y1    Y2    Y3 
# FALSE FALSE FALSE 

Not a matrix. Here the function definition is not required, we could instead just pass the is.matrix function, as it only takes one argument and has already been wrapped up in a function for us. Let’s check they are vectors as we might expect.

# show code and output
apply(mdata, 2, is.vector)
#   Y1   Y2   Y3 
# TRUE TRUE TRUE 

Why then did we need to wrap up our length function? When we want to define our own handling function for apply, we must at a minimum give a name to the incoming data, so we can use it in our function.

# show code and output
# apply(mdata, 2, length(x[x<0]))
# Error in match.fun(FUN) : object ‘x’ not found

We are referring to some value x in the function, but R does not know where that is and so gives us an error. There are other forces at play here, but for simplicity just remember to wrap any code up in a function. For example, let’s look at the mean value of only the positive values:

# show code and output
apply(mdata, 2, function(x) mean(x[x>0]))
#        Y1        Y2        Y3 
# 0.5787328 1.8871329 4.9872532 

下面计算一个稍微复杂点的例子,按行循环,让数据框的x1列加1,并计算出x1,x2列的均值。

# 生成data.frame
x <- cbind(x1 = 3, x2 = c(4:1, 2:5)); x

# 自定义函数myFUN,第一个参数x为数据
# 第二、三个参数为自定义参数,可以通过apply的'...'进行传入。
myFUN<- function(x, c1, c2) {
   c(sum(x[c1],1), mean(x[c2])) 
 }

# 把数据框按行做循环,每行分别传递给myFUN函数,设置c1,c2对应myFUN的第二、三个参数
apply(x,1,myFUN,c1='x1',c2=c('x1','x2'))

# method 2: for loop 
#定义一个结果的数据框
df<-data.frame()

# 定义for循环
for(i in 1:nrow(x)){
   row<-x[i,]                                         # 每行的值
   df<-rbind(df,rbind(c(sum(row[1],1), mean(row))))   # 计算,并赋值到结果数据框
}

# 打印结果数据框
df

# method 3: r vector calculation
data.frame(x1=x[,1]+1,x2=rowMeans(x))

# 比较一下3种操作上面性能上的消耗
# 清空环境变量
rm(list=ls())

# 封装fun1
fun1<-function(x){
   myFUN<- function(x, c1, c2) {
     c(sum(x[c1],1), mean(x[c2])) 
   }
   apply(x,1,myFUN,c1='x1',c2=c('x1','x2'))
 }

# 封装fun2
fun2<-function(x){
   df<-data.frame()
   for(i in 1:nrow(x)){
     row<-x[i,]
     df<-rbind(df,rbind(c(sum(row[1],1), mean(row))))
   }
 }

# 封装fun3
fun3<-function(x){
   data.frame(x1=x[,1]+1,x2=rowMeans(x))
 }

# 生成数据集
x <- cbind(x1=3, x2 = c(400:1, 2:500))

# 分别统计3种方法的CPU耗时。
system.time(fun1(x))
# 用户 系统 流逝 
# 0.01 0.00 0.02 

system.time(fun2(x))
# 用户 系统 流逝 
# 0.19 0.00 0.18 

system.time(fun3(x))
# 用户 系统 流逝 
#    0    0    0 

# 从CPU的耗时来看,用for循环实现的计算是耗时最长的,apply实现的循环耗时很短,而直接使用R语言内置的向量计算的操作几乎不耗时。
# 通过上面的测试,对同一个计算来说,优先考虑R语言内置的向量计算,必须要用到循环时则使用apply函数,应该尽量避免显示的使用for,while等操作方法。

2. lapply

lapply函数是一个最基础循环操作函数之一,用来对list、data.frame数据集进行循环,并返回和X长度同样的list结构作为结果集,通过lapply的开头的第一个字母’l’就可以判断返回结果集的类型。

2.1 lapply使用方式

lapply(X, FUN, ...) Apply a Function over a List or Vector(data.frame), it will return a list rather than a vector. 对列表应用函数,返回列表.

对列表或者向量使用函数,通过对x的每一个元素运用函数,生成一个与元素个数相同的值列表。lapply的返回值是和一个和X有相同的长度的list对象。这个list对象中的每个元素是将函数FUN应用到X的每一个元素,X为List对象(该list的每个元素都是一个向量),表示一个向量或者表达式对象,其他类型的对象会被R通过函数as.list()强制转换为list类型。

函数lapply是sapply函数的一个特殊情形,对一些参数的值进行了一些限定,其使用格式为:sapply(X, FUN,…, simplify = TRUE, USE.NAMES = TRUE)

sapply(, simplify = FALSE, USE.NAMES = FALSE)lapply()的返回值是相同的。如果参数simplify=TRUE,则函数sapply的返回值不是一个list,而是一个矩阵;若simplify=FALSE,则函数sapply的返回值仍然是一个list

2.2 lapply示例代码

# create list
data_list <- list(a = 1:10, 
           beta = exp(-3:3), 
           logic = c(TRUE,FALSE,FALSE,TRUE))
data_list
# $a
#  [1]  1  2  3  4  5  6  7  8  9 10
# 
# $beta
# [1]  0.04978707  0.13533528  0.36787944  1.00000000  2.71828183  7.38905610
# [7] 20.08553692
# 
# $logic
# [1]  TRUE FALSE FALSE  TRUE

lapply(data_list, mean)
# $a
# [1] 5.5
# 
# $beta
# [1] 4.535125
# 
# $logic
# [1] 0.5

lapply(data_list, quantile)
# $a
#    0%   25%   50%   75%  100% 
#  1.00  3.25  5.50  7.75 10.00 
# 
# $beta
#          0%         25%         50%         75%        100% 
#  0.04978707  0.25160736  1.00000000  5.05366896 20.08553692 
# 
# $logic
#   0%  25%  50%  75% 100% 
#  0.0  0.0  0.5  1.0  1.0 
  
lapply(1:3, function(x) x^2)
#[[1]]
#[1] 1
#
#[[2]]
#[1] 4
#
#[[3]]
#[1] 9

# And you can use unlist with lapply to get a vector.
unlist(lapply(1:3, function(x) x^2))
#[1] 1 4 9


#示例代码,计算list中的每个KEY对应该的数据的分位数。

# 构建一个list数据集x,分别包括a,b,c 三个KEY值。
x <- list(a = 1:10, 
          beta = exp(-3:3), 
          logic = c(TRUE,FALSE,FALSE,TRUE));x
# $a
#  [1]  1  2  3  4  5  6  7  8  9 10
# 
# $beta
# [1]  0.04978707  0.13533528  0.36787944  1.00000000  2.71828183
# [6]  7.38905610 20.08553692
# 
# $logic
# [1]  TRUE FALSE FALSE  TRUE

# 分别计算每个KEY对应该的数据的分位数。
lapply(x, quantile)
# $a
#    0%   25%   50%   75%  100% 
#  1.00  3.25  5.50  7.75 10.00 
# 
# $beta
#          0%         25%         50%         75%        100% 
#  0.04978707  0.25160736  1.00000000  5.05366896 20.08553692 
# 
# $logic
#   0%  25%  50%  75% 100% 
#  0.0  0.0  0.5  1.0  1.0 

apply就可以很方便地把list数据集进行循环操作了,还可以用data.frame数据集按列进行循环,但如果传入的数据集是一个向量或矩阵对象,那么直接使用lapply就不能达到想要的效果了

比如,对矩阵的列求和。

# 生成一个矩阵
x <- cbind(x1=3, x2=c(2:1,4:5))
x; class(x)
#      x1 x2
# [1,]  3  2
# [2,]  3  1
# [3,]  3  4
# [4,]  3  5
# [1] "matrix"

# 求和
lapply(x, sum)
# [[1]]
# [1] 3
# 
# [[2]]
# [1] 3
# 
# [[3]]
# [1] 3
# 
# [[4]]
# [1] 3
# 
# [[5]]
# [1] 2
# 
# [[6]]
# [1] 1
# 
# [[7]]
# [1] 4
# 
# [[8]]
# [1] 5

# lapply会分别循环矩阵中的每个值,而不是按行或按列进行分组计算。

# 如果对数据框的列求和。lapply会自动把数据框按列进行分组,再进行计算。
lapply(data.frame(x), sum)
# $x1
# [1] 12
# 
# $x2
# [1] 12

3. sapply

sapply函数是一个简化版的lapply,sapply增加了2个参数simplify和USE.NAMES,主要就是让输出看起来更友好,返回值为向量,而不是list对象。

3.1 sapply使用方式

sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE) Apply a Function over a List or Vector, 对数组、矩阵、数据框使用函数, 返回向量,等价于unlist(lapply(…)),用lapply计算,然后把结果变为向量。

这是一个用户友好版本,是lapply函数的包装版。该函数返回值为向量、矩阵,如果simplify="array",且合适的情况下,将会通过simplify2array()函数转换为矩阵。sapply(x, f, simplify=FALSE, USE.NAMES=FALSE)返回的值与lapply(x,f)是一致的。

X表示一个向量或者表达式对象,其余对象将被通过as.list强制转换为listsimplify 逻辑值或者字符串,如果可以,结果应该被简化为向量、矩阵或者高维数组。必须是命名的,不能是简写。默认值是TRUE,若合适将会返回一个向量或者矩阵。如果simplify="array",结果将返回一个矩阵 array/matrix,输出结果按数组进行分组。 USE.NAMES逻辑值,如果为TRUE,且x没有被命名,则对x进行命名。如果X为字符串,TRUE设置字符串为数据名,FALSE不设置

3.2 sapply示例代码

# show code and output
sapply(1:3, function(x) x^2)
#[1] 1 4 9

Passing simplify=FALSE to sapply will also give you a list:

# show code and output
sapply(1:3, function(x) x^2, simplify = F)
#[[1]]
#[1] 1
#
#[[2]]
#[1] 4
#
#[[3]]
#[1] 9

4. vapply

Apply a Function over a List or Vector 对列表或者向量使用函数 vapply(X, FUN, FUN.VALUE, …, USE.NAMES = TRUE) ### 使用方式 ### 示例代码

5. tapply

5.1 tapply 使用方式

tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE) Apply a Function Over a Ragged Array 对不规则阵列使用函数.

其中X通常是一向量; INDEX是一个list对象,且该list中的每一个元素都是与X有同样长度的因子; FUN是需要计算的函数; simplify是逻辑变量,若取值为TRUE(默认值),且函数FUN的计算结果总是为一个标量值,那么函数tapply返回一个数组;若取值为FALSE,则函数tapply的返回值为一个list对象。

需要注意的是,当第二个参数INDEX不是因子时,函数tapply()同样有效,因为必要时 R 会用as.factor()把参数强制转换成因子。一般用于factor,利用tapply可以实现excel数据透视表的功能.

5.2 tapply 示例代码

height <- c(174, 165, 180, 171, 160)
sex<-c("F","F","M","F","M")

tapply(height, sex, mean)
#   F   M 
# 170 170 
# tapply ex 2
n <- 17; fac <- factor(rep(1:3, length = n), levels = 1:5);fac
 # [1] 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
# Levels: 1 2 3 4 5
table(fac)
# fac
# 1 2 3 4 5 
# 6 6 5 0 0 

tapply(1:n, fac, sum)
 # 1  2  3  4  5 
# 51 57 45 NA NA 

tapply(1:n, fac, sum, simplify = FALSE)
# $`1`
# [1] 51
# 
# $`2`
# [1] 57
# 
# $`3`
# [1] 45
# 
# $`4`
# NULL
# 
# $`5`
# NULL

tapply(1:n, fac, range)
# $`1`
# [1]  1 16
# 
# $`2`
# [1]  2 17
# 
# $`3`
# [1]  3 15
# 
# $`4`
# NULL
# 
# $`5`
# NULL

tapply(1:n, fac, quantile)
# $`1`
#    0%   25%   50%   75%  100% 
#  1.00  4.75  8.50 12.25 16.00 
# 
# $`2`
#    0%   25%   50%   75%  100% 
#  2.00  5.75  9.50 13.25 17.00 
# 
# $`3`
#   0%  25%  50%  75% 100% 
#    3    6    9   12   15 
# 
# $`4`
# NULL
# 
# $`5`
# NULL

6. eapply

对一个环境空间中的所有变量进行遍历。如果我们有好的习惯,把自定义的变量都按一定的规则存储到自定义的环境空间中,那么这个函数将会让你的操作变得非常方便。当然,可能很多人都不熟悉空间的操作,那么请参考文章 揭开R语言中环境空间的神秘面纱解密R语言函数的环境空间

6.1 eapply使用方式

eapply(env, FUN, ..., all.names = FALSE, USE.NAMES = TRUE),Apply a Function Over Values in an Environment对环境中的值使用函数。

eapply函数通过对environment中命名值进行FUN计算后返回一个列表值,用户可以请求所有使用过的命名对象。env将被使用的环境,all.names 逻辑值,指示是否对所有值使用该函数, USE.NAMES 逻辑值,指示返回的列表结果是否包含命名

6.2 eapply示例代码

require(stats)

# 定义一个环境空间
env <- new.env(hash = FALSE) # so the order is fixed

# 向这个环境空间中存入3个变量
env$a <- 1:10
env$beta <- exp(-3:3)
env$logic <- c(TRUE, FALSE, FALSE, TRUE)

# what have we there?查看env空间中的变量
ls(env)

# 查看env空间中的变量字符串结构
ls.str(env)

utils::ls.str(env)
# a :  int [1:10] 1 2 3 4 5 6 7 8 9 10
# beta :  num [1:7] 0.0498 0.1353 0.3679 1 2.7183 ...
# logic :  logi [1:4] TRUE FALSE FALSE TRUE

# compute the mean for each list element
eapply(env, mean)
# $logic
# [1] 0.5
# 
# $beta
# [1] 4.535125
# 
# $a
# [1] 5.5

unlist(eapply(env, mean, USE.NAMES = FALSE))
# [1] 0.500000 4.535125 5.500000

# median and quartiles for each element (making use of "..." passing):
eapply(env, quantile, probs = 1:3/4)
# $logic
# 25% 50% 75% 
# 0.0 0.5 1.0 
# 
# $beta
#       25%       50%       75% 
# 0.2516074 1.0000000 5.0536690 
# 
# $a
#  25%  50%  75% 
# 3.25 5.50 7.75 

eapply(env, quantile)
# $logic
#   0%  25%  50%  75% 100% 
#  0.0  0.0  0.5  1.0  1.0 
# 
# $beta
#          0%         25%         50%         75%        100% 
#  0.04978707  0.25160736  1.00000000  5.05366896 20.08553692 
# 
# $a
#    0%   25%   50%   75%  100% 
#  1.00  3.25  5.50  7.75 10.00 

# 再计算中当前环境空间中的所有变量的占用内存大小。
# 查看当前环境空间中的变量
ls()

# 查看所有变量的占用内存大小
eapply(environment(), object.size)

eapply函数平时很难被用到,但对于R包开发来说,环境空间的使用是必须要掌握的。特别是当R要做为工业化的工具时,对变量的精确控制和管理是非常必要的。

7. mapply

7.1 mapply使用方式

mapply(FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE), Apply a Function to Multiple List or Vector Arguments.对多个列表或者向量参数使用函数

mapply是sapply的多变量版本。将对…中的每个参数运行FUN函数,如有必要,参数将被循环。

MoreArgs FUN函数的其他参数列表 SIMPLIFY 逻辑或者字符串,可以减少结果成为一个向量、矩阵或者更高维阵列,详见sapply的simplify参数 USE.NAMES逻辑值,如果第一个参数…已被命名,将使用这个字符向量作为名字

7.2 示例代码

mapply(rep, 1:4, 4:1)
# [[1]]
# [1] 1 1 1 1
# 
# [[2]]
# [1] 2 2 2
# 
# [[3]]
# [1] 3 3
# 
# [[4]]
# [1] 4

mapply(function(x, y) seq_len(x) + y,
       c(a =  1, b = 2, c = 3),  # names from first
       c(A = 10, B = 0, C = -10))
# $a
# [1] 11
# 
# $b
# [1] 1 2
# 
# $c
# [1] -9 -8 -7

8. rapply

8.1 rapply使用方式

rapply(X, FUN, classes = "ANY", deflt = NULL, how = c("unlist", "replace", "list"), ...) 运用函数递归产生列表,rapply是lapply的递归版本.Recursively Apply a Function to a List. X 一个列表 classes 关于类名的字符向量,或者为any时则匹配任何类 deflt 默认结果,如果使用了how=”replace”,则不能使用 how 字符串匹配三种可能结果

8.2 rapply示例代码

However the behviour is not as clean when things have names, so best to use sapply or lapply as makes sense for your data and what you want to receive back. If you want a list returned, use lapply. If you want a vector, use sapply. Dirty Deeds

Anyway, a cheap trick is to pass sapply a vector of indexes and write your function making some assumptions about the structure of the underlying data. Let’s look at our mean example again:

# show code and output
sapply(1:3, function(x) mean(m[,x]))
#[1] -0.02664418  1.95812458  4.86857792

We pass the column indexes (1,2,3) to our function, which assumes some variable m has our data. Fine for quickies but not very nice, and will likely turn into a maintainability bomb down the line. We can neaten things up a bit by passing our data in an argument to our function, and using the … special argument which all the apply functions have for passing extra arguments:

# show code and output
sapply(1:3, function(x, y) mean(y[,x]), y=m)
#[1] -0.02664418  1.95812458  4.86857792

This time, our function has 2 arguments, x and y. The x variable will be as it was before, whatever sapply is currently going through. The y variable we will pass using the optional arguments to sapply.

In this case we have passed in m, explicitly naming the y argument in the sapply call. Not strictly necessary but it makes for easier to read & maintain code. The y value will be the same for each call sapply makes to our function.

I don’t really recommend passing the index arguments like this, it is error prone and can be quite confusing to others reading your code.

I hope you found these examples helpful. Please check out part 2 where we create a density plot of the values in our matrix.

tapply group summary–函数tapply进行分组统计

tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)

对各因子应用函数(也就是分组计算),这个也常用.

其中X通常是一向量;INDEX是一个list对象,且该list中的每一个元素都是与X有同样长度的因子;FUN是需要计算的函数;

simplify是逻辑变量,若取值为TRUE(默认值),且函数FUN的计算结果总是为一个标量值,那么函数tapply返回一个数组;

若取值为FALSE,则函数tapply的返回值为一个list对象。

需要注意的是,当第二个参数INDEX不是因子时,函数 tapply() 同样有效,因为必要时 R 会用 as.factor()把参数强制转换成因子。

示例代码:

fac <- factor(rep(1:5, length = 17), levels = 1:5);fac
 # [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2
# Levels: 1 2 3 4 5

tapply(1:17, fac, sum)
#  1  2  3  4  5 
# 34 38 24 27 30 

tapply(1:17, fac, sum, simplify = FALSE)
# $`1`
# [1] 34
# 
# $`2`
# [1] 38
# 
# $`3`
# [1] 24
# 
# $`4`
# [1] 27
# 
# $`5`
# [1] 30

tapply(1:17, fac, range)
# $`1`
# [1]  1 16
# 
# $`2`
# [1]  2 17
# 
# $`3`
# [1]  3 13
# 
# $`4`
# [1]  4 14
# 
# $`5`
# [1]  5 15

利用tapply实现类似于excel里的数据透视表的功能

year <- c(2007, 2007, 2007, 2007, 2008, 2008, 2008, 2009, 2009,2009)
province <- c("A", "B", "C", "D", "A", "C", "D", "B", "C", "D")
sale <- 1:10

mydata <- data.frame(year, province, sale);mydata

attach(mydata)

tapply(sale, list(year, province))
# [1]  1  4  7 10  2  8 11  6  9 12

tapply(sale,list(year,province),mean)
#       A  B C  D
# 2007  1  2 3  4
# 2008  5 NA 6  7
# 2009 NA  8 9 10

函数table(求因子出现的频数):

使用格式为:table(..., exclude = if (useNA == "no") c(NA, NaN), useNA = c("no","ifany", "always"), dnn = list.names(...), deparse.level = 1) 其中参数exclude表示哪些因子不计算。

示例代码:

d <- factor(rep(c("A","B","C"), 10), 
            levels=c("A","B","C","D","E")); d
 # [1] A B C A B C A B C A B C A B C A B C A B C A B C A B C A B C
# Levels: A B C D E

table(d)
# d
 # A  B  C  D  E
# 10 10 10  0  0

table(d, exclude="B")
# d
 # A  C  D  E
# 10 10  0  0

sapply

函数sapply是函数lapply的一个特殊情形,对一些参数的值进行了一些限定,其使用格式为:sapply(X, FUN,..., simplify = TRUE, USE.NAMES = TRUE)

sapply(*, simplify = FALSE, USE.NAMES = FALSE)lapply(*)的返回值是相同的。如果参数simplify=TRUE,则函数sapply的返回值不是一个list,而是一个矩阵;若simplify=FALSE,则函数sapply的返回值仍然是一个list。sapply对列表应用函数,返回向量,这个比较常用,等价于unlist(lapply(…)),用lapply计算,然后把结果变为向量;

sapply(x, quantile,simplify=FALSE,use.names=FALSE)
# $a
#    0%   25%   50%   75%  100% 
#  1.00  3.25  5.50  7.75 10.00 
# 
# $beta
#          0%         25%         50%         75%        100% 
#  0.04978707  0.25160736  1.00000000  5.05366896 20.08553692 
# 
# $logic
#   0%  25%  50%  75% 100% 
#  0.0  0.0  0.5  1.0  1.0 

#参数simplify=TRUE的情况,sapply(x, quantile, simplify = 1),
# default: sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
sapply(x, quantile)
#          a        beta logic
# 0%    1.00  0.04978707   0.0
# 25%   3.25  0.25160736   0.0
# 50%   5.50  1.00000000   0.5
# 75%   7.75  5.05366896   1.0
# 100% 10.00 20.08553692   1.0

?sapply

函数mapply

函数mapply是函数sapply的变形版,mapply 将函数 FUN 依次应用每一个参数的第一个元素、第二个元素、第三个元素上。

函数mapply的使用格式如下:mapply(FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE,USE.NAMES = TRUE)其中参数MoreArgs表示函数FUN的参数列表。

示例代码:

## times argument is for rep()
mapply(rep, times=1:4, x=4:1)
# [[1]]
# [1] 4
# 
# [[2]]
# [1] 3 3
# 
# [[3]]
# [1] 2 2 2
# 
# [[4]]
# [1] 1 1 1 1

#直接使用函数rep的结果:
rep(1:4,1:4)
# [1] 1 2 2 3 3 3 4 4 4 4

aggreate

aggreate:split-apply-combine,拆分成子集,分别计算合并结果输出;

LS0tDQp0aXRsZTogIlVzaW5nX2FwcGx5X3NhcHBseV9sYXBwbHlfaW5fUiINCmF1dGhvcjogIlN0b25lX0hvdSINCmRhdGU6ICIyMDE25bm0OeaciDPml6UiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IA0KICAgIHRvYzogeWVzDQotLS0NCg0KIyBVc2luZ19hcHBseV9zYXBwbHlfbGFwcGx5X3RhcHBseV9tYXBwbHlfaW5fUg0KDQo+IFJlZmVyZW5jZTogW2h0dHA6Ly9ibG9nLmZlbnMubWUvci1hcHBseS9dKGh0dHA6Ly9ibG9nLmZlbnMubWUvci1hcHBseS8pDQoNCmFwcGx555qE5a625peP5Ye95pWwDQoNCmFwcGx55Ye95pWw5peP5pivUuivreiogOS4reaVsOaNruWkhOeQhueahOS4gOe7hOaguOW/g+WHveaVsO+8jOmAmui/h+S9v+eUqGFwcGx55Ye95pWw77yM5oiR5Lus5Y+v5Lul5a6e546w5a+55pWw5o2u55qE5b6q546v44CB5YiG57uE44CB6L+H5ruk44CB57G75Z6L5o6n5Yi2562J5pON5L2c44CC5L2G5piv77yM55Sx5LqO5ZyoUuivreiogOS4rWFwcGx55Ye95pWw5LiO5YW25LuW6K+t6KiA5b6q546v5L2T55qE5aSE55CG5oCd6Lev5piv5a6M5YWo5LiN5LiA5qC355qE77yM5omA5LulYXBwbHnlh73mlbDml4/kuIDnm7TmmK/kvb/nlKjogIXnjqnkuI3ovazkuIDnsbvmoLjlv4Plh73mlbDjgIINCg0K5b6I5aSaUuivreiogOaWsOaJi++8jOWGmeS6huW+iOWkmueahGZvcuW+queOr+S7o+egge+8jOS5n+S4jeaEv+aEj+WkmuiKseeCueaXtumXtOaKimFwcGx55Ye95pWw55qE5L2/55So5pa55rOV5LqG6Kej5riF5qWa77yM5pyA5ZCO5oqKUuS7o+eggeWGmeeahOi3n0PkvLzlvpfvvIzmiJHkuKXph43phJnop4blj6rkvJrlhplmb3LnmoRS56iL5bqP5ZGY44CCDQoNCmFwcGx55Ye95pWw5pys6Lqr5bCx5piv6Kej5Yaz5pWw5o2u5b6q546v5aSE55CG55qE6Zeu6aKY77yM5Li65LqG6Z2i5ZCR5LiN5ZCM55qE5pWw5o2u57G75Z6L77yM5LiN5ZCM55qE6L+U5Zue5YC877yMYXBwbHnlh73mlbDnu4TmiJDkuobkuIDkuKrlh73mlbDml4/vvIzljIXmi6zkuoY45Liq5Yqf6IO957G75Ly855qE5Ye95pWw44CC6L+Z5YW25Lit5pyJ5Lqb5Ye95pWw5b6I55u45Ly877yM5pyJ5Lqb5Lmf5LiN5piv5aSq5LiA5qC355qE44CCDQoNCkdyb3VwIDAxLeWIhue7hOiuoeeul++8mg0KdGFwcGx5OiBpbnB1dDp2ZWN0b3IsIG91dHB1dDp2ZWN0b3INCmFwcGx5OiBpbnB1dDpsaXN0LGRhdGEuZnJhbWUsYXJyYXksIG91dDp2ZWN0b3IsbWFydGl4DQoNCkdyb3VwIDAyLeWkmuWPguaVsOiuoeeul++8mg0KbWFwcGx5OmlucHV0OnZlY3RvcihtdWx0aSksIG91dHB1dDp2ZWN0b3IsbWFydHJpeA0KDQpHcm91cCAwMy3lvqrnjq/ov63ku6PvvJoNCmxhcHBsee+8mmlucHV0Omxpc3QsZGF0YS5mcmFtZe+8jG91dHB1dDpsaXN0DQpHcm91cCAwMy4xLWxhcHBseeeahOeugOWMlueJiA0Kc2FwcGx5IC0+IGlucHV0Omxpc3QsZGF0YS5mcmFtZe+8jG91dHB1dDp2ZWN0b3IsIG1hcnRyaXgNCnNhcHBseSDlj6/orr7nva7ov5Tlm57lgLwgLT4gaW5wdXQ6bGlzdCxkYXRhLmZyYW1l77yMb3V0cHV0OnZlY3RvciwgbWFydHJpeA0KR3JvdXAgMDMuMi1sYXBwbHnnmoTpgJLlvZLniYggcmFwcGx5IC0+DQppbnB1dDpsaXN077yMb3V0cHV0Omxpc3QNCg0KR3JvdXAgMDQt546v5aKD56m66Ze06YGN5Y6GDQplYXBwbHk6aW5wdXQ6ZW52aXJvbm1lbnTvvIxvdXRwdXQ6bGlzdA0KDQrmnIDluLjnlKjnmoTlh73mlbDkuLphcHBseeWSjHNhcHBseQ0KDQojIyAwLiBEYXRhDQoNCldlIGNhbiBzaW11bGF0ZSB0aGlzIGRhdGEgdXNpbmcgcm5vcm0sIHRvIGNyZWF0ZSB0aHJlZSBzZXRzIG9mIG9ic2VydmF0aW9ucy4gVGhlIGZpcnN0IGhhcyBtZWFuIDAsIHNlY29uZCBtZWFuIG9mIDIsIHRoaXJkIG9mIG1lYW4gb2YgNSwgYW5kIHdpdGggMzAgcm93cy4NCg0KYGBge3IgbWF0cml4IGV4MSwgZWNobz1UUlVFfQ0KIyBjcmVhdGUgZGF0YQ0Kc2V0LnNlZWQoMzApDQoNCiMgbWF0cml4DQpkYXRhIDwtIGNiaW5kKHJub3JtKDMwLCAwKSwgDQogICAgICAgICAgICAgIHJub3JtKDMwLCAyKSwgDQogICAgICAgICAgICAgIHJub3JtKDMwLCA1KSkNCiMgbWF0cml4IHJvdyBhbmQgY29sdW1uDQpkYXRhIDwtIG1hdHJpeChkYXRhLCBucm93PTMwLCBuY29sPTMpDQoNCiMgY2hlY2sgZGF0YQ0KaGVhZChkYXRhLCA1KTt0YWlsKGRhdGEsIDUpDQojICAgICAgICAgICAgWywxXSAgICAgIFssMl0gICAgIFssM10NCiMgWzEsXSAtMS4yODg1MTgyIDAuMjc0Nzk3NSA1Ljk3NDA1Ng0KIyBbMixdIC0wLjM0NzY4OTQgMi42MTQ4NjA3IDUuMzk5MTg1DQojIFszLF0gLTAuNTIxNjI4OCAyLjcyNjg3NTEgNS4zNDgyNTENCiMgWzQsXSAgMS4yNzM0NzMyIDEuOTU3ODA5OCA0LjcwOTUwOQ0KIyBbNSxdICAxLjgyNDUyMDYgMi4yMTYwMDE4IDYuNTc2NjIyDQojICAgICAgICAgICAgIFssMV0gICAgIFssMl0gICAgIFssM10NCiMgWzI2LF0gLTEuMDk2NDAyMCAxLjA4NDQ3MyAyLjk2NjQyMA0KIyBbMjcsXSAtMC41MzQyMjA3IDQuNTk4NDAyIDMuOTM0MDQ3DQojIFsyOCxdIC0xLjQyMTIwMzAgMS40ODgzODcgNi41NzIxMjUNCiMgWzI5LF0gLTEuMjQyNzM4MyAxLjEzMzY0NiA0LjI2OTU1MQ0KIyBbMzAsXSAgMC4yMzE5MzYyIDIuMzg3MjE4IDUuNDUwNTE2DQoNCmBgYA0KDQojIyAxLiBhcHBseQ0KDQphcHBseeWHveaVsOaYr+acgOW4uOeUqOeahOS7o+abv2ZvcuW+queOr+eahOWHveaVsOOAgmFwcGx55Ye95pWw5Y+v5Lul5a+555+p6Zi144CB5pWw5o2u5qGG44CB5pWw57uEKOS6jOe7tOOAgeWkmue7tCnvvIzmjInooYzmiJbliJfov5vooYzlvqrnjq/orqHnrpfvvIzlr7nlrZDlhYPntKDov5vooYzov63ku6PvvIzlubbmiorlrZDlhYPntKDku6Xlj4LmlbDkvKDpgJLnmoTlvaLlvI/nu5noh6rlrprkuYnnmoRGVU7lh73mlbDkuK3vvIzlubbku6Xov5Tlm57orqHnrpfnu5PmnpzjgIINCg0KIyMjIDEuMSBhcHBseeS9v+eUqOaWueW8jw0KDQpgYXBwbHkoWCwgTUFSR0lOLCBGVU4sIC4uLilgIEFwcGx5IEZ1bmN0aW9ucyBPdmVyIEFycmF5IE1hcmdpbnMsIOWvuSoq5pWw57uE44CB55+p6Zi144CB5pWw5o2u5qGGKirnmoTooYwx5oiW6ICF5YiXMuS9v+eUqOWHveaVsCzmjInooYzmiJbogIXmjInliJfov5vooYzorqHnrpfjgILlhbbkuK1Y5Li65LiA5Liq5pWw57uE77ybDQoNCk1BUkdJTuS4uuS4gOS4quWQkemHj++8iOihqOekuuimgeWwhuWHveaVsEZVTuW6lOeUqOWIsFjnmoTooYzov5jmmK/liJfvvInvvIzoi6XkuLox6KGo56S65Y+W6KGM77yM5Li6MuihqOekuuWPluWIl++8jOS4umMoMSwyKeihqOekuuihjOOAgeWIl+mDveiuoeeul+OAgg0KDQojIyMgMS4yIGFwcGx55Ye95pWw56S65L6L5Luj56CB77yaDQpgYGB7ciBhcHBseSBleDF9DQpkYXRhX2FycmF5IDwtIG1hdHJpeCgxOjIwLCBuY29sID0gNCk7ZGF0YV9hcnJheQ0KIyAgICAgIFssMV0gWywyXSBbLDNdIFssNF0NCiMgWzEsXSAgICAxICAgIDYgICAxMSAgIDE2DQojIFsyLF0gICAgMiAgICA3ICAgMTIgICAxNw0KIyBbMyxdICAgIDMgICAgOCAgIDEzICAgMTgNCiMgWzQsXSAgICA0ICAgIDkgICAxNCAgIDE5DQojIFs1LF0gICAgNSAgIDEwICAgMTUgICAyMA0KDQphcHBseShkYXRhX2FycmF5LCAxLCBtZWFuKQ0KIyBbMV0gIDguNSAgOS41IDEwLjUgMTEuNSAxMi41DQoNCmFwcGx5KGRhdGFfYXJyYXksIDIsIG1lYW4pDQojIFsxXSAgMyAgOCAxMyAxOA0KDQptYSA8LSBtYXRyaXgoYygxOjQsIDEsIDY6OCksIG5yb3cgPSAyKTttYQ0KIyAgICAgIFssMV0gWywyXSBbLDNdIFssNF0NCiMgWzEsXSAgICAxICAgIDMgICAgMSAgICA3DQojIFsyLF0gICAgMiAgICA0ICAgIDYgICAgOA0KDQojIGMoMSwyKeihqOekuuihjOOAgeWIl+mDveiuoeeulw0KYXBwbHkobWEsIGMoMSwyKSwgc3VtKQ0KIyAgICAgIFssMV0gWywyXSBbLDNdIFssNF0NCiMgWzEsXSAgICAxICAgIDMgICAgMSAgICA3DQojIFsyLF0gICAgMiAgICA0ICAgIDYgICAgOA0KDQojIDJuZCBwYXJhbWV0ZXIgMeihqOekunJvd+iuoeeulw0KYXBwbHkobWEsIDEsIHN1bSkNCiMgWzFdIDEyIDIwDQoNCiMgMm5kIHBhcmFtZXRlciAy6KGo56S6Y29sdW1u6K6h566XDQphcHBseShtYSwgMiwgc3VtKQ0KIyBbMV0gIDMgIDcgIDcgMTUNCg0KDQp4IDwtIGNiaW5kKHgxID0gMywgeDIgPSBjKDQ6MSwgMjo1KSk7eA0KIyAgICAgIHgxIHgyDQojIFsxLF0gIDMgIDQNCiMgWzIsXSAgMyAgMw0KIyBbMyxdICAzICAyDQojIFs0LF0gIDMgIDENCiMgWzUsXSAgMyAgMg0KIyBbNixdICAzICAzDQojIFs3LF0gIDMgIDQNCiMgWzgsXSAgMyAgNQ0KZGltbmFtZXMoeClbWzFdXSA8LSBsZXR0ZXJzWzE6OF07eA0KIyAgIHgxIHgyDQojIGEgIDMgIDQNCiMgYiAgMyAgMw0KIyBjICAzICAyDQojIGQgIDMgIDENCiMgZSAgMyAgMg0KIyBmICAzICAzDQojIGcgIDMgIDQNCiMgaCAgMyAgNQ0KDQphcHBseSh4LCAyLCBtZWFuLCB0cmltID0gLjIpICPmiKrlsL7lj5bmjInliJflubPlnYflgLwNCiN0cmltID0gLjLmmK9tZWFu55qE5Y+C5pWwDQojIHgxIHgyIA0KICMgMyAgMyANCg0KIyBjYWxjdWxhdGUgY29sdW1uIHN1bXMNCmNvbC5zdW1zIDwtIGFwcGx5KHgsIDIsIHN1bSk7Y29sLnN1bXMNCiMgeDEgeDIgDQojIDI0IDI0IA0KDQojIGNhbGN1bGF0ZSByb3cgc3Vtcw0Kcm93LnN1bXMgPC0gYXBwbHkoeCwgMSwgc3VtKTtyb3cuc3Vtcw0KIyBhIGIgYyBkIGUgZiBnIGggDQojIDcgNiA1IDQgNSA2IDcgOCANCg0KIyBjYmluZCByb3cgc3Vtcw0KY2JpbmQoeCwgUnRvdCA9IHJvdy5zdW1zKQ0KIyAgIHgxIHgyIFJ0b3QNCiMgYSAgMyAgNCAgICA3DQojIGIgIDMgIDMgICAgNg0KIyBjICAzICAyICAgIDUNCiMgZCAgMyAgMSAgICA0DQojIGUgIDMgIDIgICAgNQ0KIyBmICAzICAzICAgIDYNCiMgZyAgMyAgNCAgICA3DQojIGggIDMgIDUgICAgOA0KDQojIGNhbGN1bGF0ZSBjb2x1bW4gc3Vtcw0KYyhjb2wuc3Vtcywgc3VtKGNvbC5zdW1zKSkNCiMgeDEgeDIgICAgDQojIDI0IDI0IDQ4IA0KDQojIHJvdyB0b3RhbCBzdW0gYW5kIGNvbHVtbiB0b3RhbCBzdW0NCnJiaW5kKGNiaW5kKHgsIFJvd19Ub3RhbCA9IHJvdy5zdW1zKSwgQ29sdW1uX1RvdGFsID0gYyhjb2wuc3Vtcywgc3VtKGNvbC5zdW1zKSkpDQojICAgICAgICAgICAgICB4MSB4MiBSb3dfVG90YWwNCiMgYSAgICAgICAgICAgICAzICA0ICAgICAgICAgNw0KIyBiICAgICAgICAgICAgIDMgIDMgICAgICAgICA2DQojIGMgICAgICAgICAgICAgMyAgMiAgICAgICAgIDUNCiMgZCAgICAgICAgICAgICAzICAxICAgICAgICAgNA0KIyBlICAgICAgICAgICAgIDMgIDIgICAgICAgICA1DQojIGYgICAgICAgICAgICAgMyAgMyAgICAgICAgIDYNCiMgZyAgICAgICAgICAgICAzICA0ICAgICAgICAgNw0KIyBoICAgICAgICAgICAgIDMgIDUgICAgICAgICA4DQojIENvbHVtbl9Ub3RhbCAyNCAyNCAgICAgICAgNDgNCg0KYGBgDQoNCmFwcGx5IDJuZCBleGFtcGxlDQpgYGB7ciBhcHBseSBleDIsIGVjaG89VFJVRX0NCiMgUmFuZG9tIE51bWJlciBHZW5lcmF0aW9uLCByZXByb2R1Y3Rpb24NCnNldC5zZWVkKDEwKQ0KDQojIGNyZWF0ZWQgdGhhdCBtYXRyaXggY29ycmVjdGx5LCB0aHJlZSBjb2x1bW5zIGVhY2ggd2l0aCBhIG1lYW4gMCwgMiBhbmQgNSByZXNwZWN0aXZlbHkuIA0KIyBXZSBjYW4gdXNlIGFwcGx5IGFuZCB0aGUgYmFzZSBtZWFuIGZ1bmN0aW9uIHRvIGNoZWNrIHRoaXMuDQpZMSA8LSBybm9ybSgzMCwgbWVhbiA9IDAsIHNkID0gMSkNClkyIDwtIHJub3JtKDMwLCBtZWFuID0gMiwgc2QgPSAxKQ0KWTMgPC0gcm5vcm0oMzAsIG1lYW4gPSA1LCBzZCA9IDEpDQoNCiMgQ3JlYXRlIGRhdGEgZnJhbWUNCm1kYXRhIDwtIGRhdGEuZnJhbWUoWTEsIFkyLCBZMyk7bWRhdGENCg0KIyBhcyBtYXRyaXggYW5kIHVzZSBhcHBseSB0byByb3dzDQptZGF0YSA8LSBhcy5tYXRyaXgobWRhdGEpDQoNCiMgUGFzc2luZyBhIDEgaW4gdGhlIHNlY29uZCBhcmd1bWVudCwgd2UgZ2V0IDMwIHZhbHVlcyBiYWNrLA0KIyBnaXZpbmcgdGhlIG1lYW4gb2YgZWFjaCByb3cuIA0KIyBOb3QgdGhlIHRocmVlIG51bWJlcnMgd2Ugd2VyZSBleHBlY3RpbmcsIHRyeSBhZ2Fpbi4NCmFwcGx5KG1kYXRhLCAxLCBtZWFuKQ0KIyAgWzFdIDEuMzA5MTM3IDIuMDkzODc1IDEuOTIyMzA0IDIuMzA4NjI1IDIuMzI2OTkzIDIuMzkwMTM1IDIuMjk2NTY3DQojICBbOF0gMS40NjUzNDMgMS44NzA1MDkgMS42MTUyMTMgMy4xODk3NTEgMS44NTQyNzAgMS42MjgyMTkgMi44Njc4MDUNCiMgWzE1XSAxLjc2MTI2NyAyLjc0NDQxMyAxLjU5OTI1MSAyLjAyNDQxOCAyLjA2NTQyMyAzLjIyMDI2NCAyLjE5ODYyNw0KIyBbMjJdIDEuNDE5MTY1IDIuODAxOTk0IDIuNTc4NDM2IDIuMjI2OTU0IDIuNTIzODM3IDIuMDIzMzMyIDIuOTYwNDE5DQojIFsyOV0gMS42ODk0NjcgMi4zMjEwODQNCg0KIyBQYXNzaW5nIGEgMiBpbiB0aGUgc2Vjb25kIGFyZ3VtZW50LCB3ZSBnZXQgMyB2YWx1ZXMgYmFjaywgRG9uZSBHcmVhdC4gDQojIFdlIGNhbiBzZWUgdGhlIG1lYW4gb2YgZWFjaCBjb2x1bW4gaXMgcm91Z2hseSAwLCAyLCBhbmQgNSBhcyB3ZSBleHBlY3RlZC4NCmFwcGx5KG1kYXRhLCAyLCBtZWFuKQ0KIyAgICAgICAgIFkxICAgICAgICAgWTIgICAgICAgICBZMyANCiMgLTAuMzQ0Njc2NCAgMS44ODcxMzI5ICA0Ljk4NzI1MzIgDQoNCg0KIyBjcmVhdGUgbWFydGl4DQojIG1kYXQgPC0gbWF0cml4KGMoMSwyLDMsIDExLDEyLDEzKSwgDQojICAgICAgICAgICAgICAgIG5yb3cgPSAyLCANCiMgICAgICAgICAgICAgICAgbmNvbCA9IDMsIA0KIyAgICAgICAgICAgICAgICBieXJvdyA9IFRSVUUsDQojICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChjKCJyb3cxIiwgInJvdzIiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkMuMSIsICJDLjIiLCAiQy4zIikpKQ0KIyBtZGF0DQoNCmBgYA0KDQojIyMgMS4zIGFwcGx5IHNlbGYgZGVmaW5lZCBmdW5jdGlvbnMNCg0KTGV04oCZcyBzYXkgSSBzZWUgdGhhdCBuZWdhdGl2ZSBudW1iZXIgYW5kIHJlYWxpc2UgSSB3YW50ZWQgdG8gb25seSBsb29rIGF0IHBvc2l0aXZlIHZhbHVlcy4gDQoNCkxldOKAmXMgc2VlIGhvdyBtYW55IG5lZ2F0aXZlIG51bWJlcnMgZWFjaCBjb2x1bW4gaGFzLCB1c2luZyBhcHBseSBhZ2FpbjoNCmBgYHtyIGFwcGx5IGV4MywgZWNobz1UUlVFfQ0KIyBsZW5ndGggYW5kIGNoZWNrIG5lZ2F0aXZlIG51bWJlcg0KbGVuZ3RoKG1kYXRhKQ0KIyBbMV0gOTANCmxlbmd0aChtZGF0YVttZGF0YTwwXSkNCiMgWzFdIDIwDQphcHBseShtZGF0YSwgMiwgZnVuY3Rpb24oeCkgbGVuZ3RoKHhbeDwwXSkpDQojIFkxIFkyIFkzIA0KIyAyMCAgMCAgMA0KYGBgDQoNClNvIDIwIG5lZ2F0aXZlIHZhbHVlcyBpbiBjb2x1bW4gb25lLCBub25lIG5lZ2F0aXZlIHZhbHVlIGluIGNvbHVtbiB0d28sIGFuZCBub25lIGluIGNvbHVtbiB0aHJlZS4gDQoNCk1vcmUgb3IgbGVzcyB3aGF0IHdlIHdvdWxkIGV4cGVjdCBmb3IgdGhyZWUgbm9ybWFsIGRpc3RyaWJ1dGlvbnMgd2l0aCB0aGUgZ2l2ZW4gbWVhbnMgYW5kIHNkIG9mIDEuDQoNCkhlcmUgd2UgaGF2ZSB1c2VkIGEgc2ltcGxlIGZ1bmN0aW9uIHdlIGRlZmluZWQgaW4gdGhlIGNhbGwgdG8gYXBwbHksIHJhdGhlciB0aGFuIHNvbWUgYnVpbHQgaW4gZnVuY3Rpb24uIA0KDQpOb3RlIHdlIGRpZCBub3Qgc3BlY2lmeSBhIHJldHVybiB2YWx1ZSBmb3Igb3VyIGZ1bmN0aW9uLiAqUiB3aWxsIG1hZ2ljYWxseSByZXR1cm4gdGhlIGxhc3QgZXZhbHVhdGVkIHZhbHVlKi4gDQoNClRoZSBhY3R1YWwgZnVuY3Rpb24gaXMgdXNpbmcgc3Vic2V0dGluZyB0byBleHRyYWN0IGFsbCB0aGUgZWxlbWVudHMgaW4geCB0aGF0IGFyZSBsZXNzIHRoYW4gMCwgYW5kIHRoZW4gY291bnRpbmcgaG93IG1hbnkgYXJlIGxlZnQgYXJlIHVzaW5nIGxlbmd0aC4NCg0KVGhlIGZ1bmN0aW9uIHRha2VzIG9uZSBhcmd1bWVudCwgd2hpY2ggSSBoYXZlIGFyYml0cmFyaWx5IGNhbGxlZCB4LiBJbiB0aGlzIGNhc2UgeCB3aWxsIGJlIGEgc2luZ2xlIGNvbHVtbiBvZiB0aGUgbWF0cml4LiBJcyBpdCBhIDEgY29sdW1uIG1hdHJpeCBvciBhIGp1c3QgYSB2ZWN0b3I/IExldOKAmXMgaGF2ZSBhIGxvb2s6DQoNCmBgYHtyIGFwcGx5IGV4NCwgZWNobz1UUlVFfQ0KIyBzaG93IGNvZGUgYW5kIG91dHB1dA0KYXBwbHkobWRhdGEsIDIsIGZ1bmN0aW9uKHgpIGlzLm1hdHJpeCh4KSkNCiMgICAgWTEgICAgWTIgICAgWTMgDQojIEZBTFNFIEZBTFNFIEZBTFNFIA0KYGBgDQoNCk5vdCBhIG1hdHJpeC4gSGVyZSB0aGUgZnVuY3Rpb24gZGVmaW5pdGlvbiBpcyBub3QgcmVxdWlyZWQsIHdlIGNvdWxkIGluc3RlYWQganVzdCBwYXNzIHRoZSBgaXMubWF0cml4YCBmdW5jdGlvbiwgYXMgaXQgb25seSB0YWtlcyBvbmUgYXJndW1lbnQgYW5kIGhhcyBhbHJlYWR5IGJlZW4gd3JhcHBlZCB1cCBpbiBhIGZ1bmN0aW9uIGZvciB1cy4gTGV04oCZcyBjaGVjayB0aGV5IGFyZSB2ZWN0b3JzIGFzIHdlIG1pZ2h0IGV4cGVjdC4NCg0KYGBge3IgYXBwbHkgZXg1LCBlY2hvPVRSVUV9DQojIHNob3cgY29kZSBhbmQgb3V0cHV0DQphcHBseShtZGF0YSwgMiwgaXMudmVjdG9yKQ0KIyAgIFkxICAgWTIgICBZMyANCiMgVFJVRSBUUlVFIFRSVUUgDQpgYGANCg0KV2h5IHRoZW4gZGlkIHdlIG5lZWQgdG8gd3JhcCB1cCBvdXIgbGVuZ3RoIGZ1bmN0aW9uPyBXaGVuIHdlIHdhbnQgdG8gZGVmaW5lIG91ciBvd24gaGFuZGxpbmcgZnVuY3Rpb24gZm9yIGFwcGx5LCB3ZSBtdXN0IGF0IGEgbWluaW11bSBnaXZlIGEgbmFtZSB0byB0aGUgaW5jb21pbmcgZGF0YSwgc28gd2UgY2FuIHVzZSBpdCBpbiBvdXIgZnVuY3Rpb24uDQoNCmBgYHtyIGFwcGx5IGV4NiwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBzaG93IGNvZGUgYW5kIG91dHB1dA0KIyBhcHBseShtZGF0YSwgMiwgbGVuZ3RoKHhbeDwwXSkpDQojIEVycm9yIGluIG1hdGNoLmZ1bihGVU4pIDogb2JqZWN0IOKAmHjigJkgbm90IGZvdW5kDQpgYGANCg0KV2UgYXJlIHJlZmVycmluZyB0byBzb21lIHZhbHVlIHggaW4gdGhlIGZ1bmN0aW9uLCBidXQgUiBkb2VzIG5vdCBrbm93IHdoZXJlIHRoYXQgaXMgYW5kIHNvIGdpdmVzIHVzIGFuIGVycm9yLiBUaGVyZSBhcmUgb3RoZXIgZm9yY2VzIGF0IHBsYXkgaGVyZSwgYnV0IGZvciBzaW1wbGljaXR5IGp1c3QgcmVtZW1iZXIgdG8gd3JhcCBhbnkgY29kZSB1cCBpbiBhIGZ1bmN0aW9uLiBGb3IgZXhhbXBsZSwgbGV04oCZcyBsb29rIGF0IHRoZSBtZWFuIHZhbHVlIG9mIG9ubHkgdGhlIHBvc2l0aXZlIHZhbHVlczoNCg0KYGBge3IgYXBwbHkgZXg3LCBlY2hvPVRSVUV9DQojIHNob3cgY29kZSBhbmQgb3V0cHV0DQphcHBseShtZGF0YSwgMiwgZnVuY3Rpb24oeCkgbWVhbih4W3g+MF0pKQ0KIyAgICAgICAgWTEgICAgICAgIFkyICAgICAgICBZMyANCiMgMC41Nzg3MzI4IDEuODg3MTMyOSA0Ljk4NzI1MzIgDQpgYGANCg0K5LiL6Z2i6K6h566X5LiA5Liq56iN5b6u5aSN5p2C54K555qE5L6L5a2Q77yM5oyJ6KGM5b6q546v77yM6K6p5pWw5o2u5qGG55qEeDHliJfliqAx77yM5bm26K6h566X5Ye6eDEseDLliJfnmoTlnYflgLzjgIINCg0KYGBge3IgYXBwbHkgZXg4LCBlY2hvPVRSVUV9DQojIOeUn+aIkGRhdGEuZnJhbWUNCnggPC0gY2JpbmQoeDEgPSAzLCB4MiA9IGMoNDoxLCAyOjUpKTsgeA0KDQojIOiHquWumuS5ieWHveaVsG15RlVO77yM56ys5LiA5Liq5Y+C5pWweOS4uuaVsOaNrg0KIyDnrKzkuozjgIHkuInkuKrlj4LmlbDkuLroh6rlrprkuYnlj4LmlbDvvIzlj6/ku6XpgJrov4dhcHBseeeahCcuLi4n6L+b6KGM5Lyg5YWl44CCDQpteUZVTjwtIGZ1bmN0aW9uKHgsIGMxLCBjMikgew0KICAgYyhzdW0oeFtjMV0sMSksIG1lYW4oeFtjMl0pKSANCiB9DQoNCiMg5oqK5pWw5o2u5qGG5oyJ6KGM5YGa5b6q546v77yM5q+P6KGM5YiG5Yir5Lyg6YCS57uZbXlGVU7lh73mlbDvvIzorr7nva5jMSxjMuWvueW6lG15RlVO55qE56ys5LqM44CB5LiJ5Liq5Y+C5pWwDQphcHBseSh4LDEsbXlGVU4sYzE9J3gxJyxjMj1jKCd4MScsJ3gyJykpDQoNCiMgbWV0aG9kIDI6IGZvciBsb29wIA0KI+WumuS5ieS4gOS4que7k+aenOeahOaVsOaNruahhg0KZGY8LWRhdGEuZnJhbWUoKQ0KDQojIOWumuS5iWZvcuW+queOrw0KZm9yKGkgaW4gMTpucm93KHgpKXsNCiAgIHJvdzwteFtpLF0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMg5q+P6KGM55qE5YC8DQogICBkZjwtcmJpbmQoZGYscmJpbmQoYyhzdW0ocm93WzFdLDEpLCBtZWFuKHJvdykpKSkgICAjIOiuoeeul++8jOW5tui1i+WAvOWIsOe7k+aenOaVsOaNruahhg0KfQ0KDQojIOaJk+WNsOe7k+aenOaVsOaNruahhg0KZGYNCg0KIyBtZXRob2QgMzogciB2ZWN0b3IgY2FsY3VsYXRpb24NCmRhdGEuZnJhbWUoeDE9eFssMV0rMSx4Mj1yb3dNZWFucyh4KSkNCg0KIyDmr5TovoPkuIDkuIsz56eN5pON5L2c5LiK6Z2i5oCn6IO95LiK55qE5raI6ICXDQojIOa4heepuueOr+Wig+WPmOmHjw0Kcm0obGlzdD1scygpKQ0KDQojIOWwgeijhWZ1bjENCmZ1bjE8LWZ1bmN0aW9uKHgpew0KICAgbXlGVU48LSBmdW5jdGlvbih4LCBjMSwgYzIpIHsNCiAgICAgYyhzdW0oeFtjMV0sMSksIG1lYW4oeFtjMl0pKSANCiAgIH0NCiAgIGFwcGx5KHgsMSxteUZVTixjMT0neDEnLGMyPWMoJ3gxJywneDInKSkNCiB9DQoNCiMg5bCB6KOFZnVuMg0KZnVuMjwtZnVuY3Rpb24oeCl7DQogICBkZjwtZGF0YS5mcmFtZSgpDQogICBmb3IoaSBpbiAxOm5yb3coeCkpew0KICAgICByb3c8LXhbaSxdDQogICAgIGRmPC1yYmluZChkZixyYmluZChjKHN1bShyb3dbMV0sMSksIG1lYW4ocm93KSkpKQ0KICAgfQ0KIH0NCg0KIyDlsIHoo4VmdW4zDQpmdW4zPC1mdW5jdGlvbih4KXsNCiAgIGRhdGEuZnJhbWUoeDE9eFssMV0rMSx4Mj1yb3dNZWFucyh4KSkNCiB9DQoNCiMg55Sf5oiQ5pWw5o2u6ZuGDQp4IDwtIGNiaW5kKHgxPTMsIHgyID0gYyg0MDA6MSwgMjo1MDApKQ0KDQojIOWIhuWIq+e7n+iuoTPnp43mlrnms5XnmoRDUFXogJfml7bjgIINCnN5c3RlbS50aW1lKGZ1bjEoeCkpDQojIOeUqOaItyDns7vnu58g5rWB6YCdIA0KIyAwLjAxIDAuMDAgMC4wMiANCg0Kc3lzdGVtLnRpbWUoZnVuMih4KSkNCiMg55So5oi3IOezu+e7nyDmtYHpgJ0gDQojIDAuMTkgMC4wMCAwLjE4IA0KDQpzeXN0ZW0udGltZShmdW4zKHgpKQ0KIyDnlKjmiLcg57O757ufIOa1gemAnSANCiMgICAgMCAgICAwICAgIDAgDQoNCiMg5LuOQ1BV55qE6ICX5pe25p2l55yL77yM55SoZm9y5b6q546v5a6e546w55qE6K6h566X5piv6ICX5pe25pyA6ZW/55qE77yMYXBwbHnlrp7njrDnmoTlvqrnjq/ogJfml7blvojnn63vvIzogIznm7TmjqXkvb/nlKhS6K+t6KiA5YaF572u55qE5ZCR6YeP6K6h566X55qE5pON5L2c5Yeg5LmO5LiN6ICX5pe244CCDQojIOmAmui/h+S4iumdoueahOa1i+ivle+8jOWvueWQjOS4gOS4quiuoeeul+adpeivtO+8jOS8mOWFiOiAg+iZkVLor63oqIDlhoXnva7nmoTlkJHph4/orqHnrpfvvIzlv4XpobvopoHnlKjliLDlvqrnjq/ml7bliJnkvb/nlKhhcHBseeWHveaVsO+8jOW6lOivpeWwvemHj+mBv+WFjeaYvuekuueahOS9v+eUqGZvcix3aGlsZeetieaTjeS9nOaWueazleOAgg0KYGBgDQoNCg0KDQoNCg0KIyMgMi4gbGFwcGx5DQoNCmxhcHBseeWHveaVsOaYr+S4gOS4quacgOWfuuehgOW+queOr+aTjeS9nOWHveaVsOS5i+S4gO+8jOeUqOadpeWvuWxpc3TjgIFkYXRhLmZyYW1l5pWw5o2u6ZuG6L+b6KGM5b6q546v77yM5bm26L+U5Zue5ZKMWOmVv+W6puWQjOagt+eahGxpc3Tnu5PmnoTkvZzkuLrnu5Pmnpzpm4bvvIzpgJrov4dsYXBwbHnnmoTlvIDlpLTnmoTnrKzkuIDkuKrlrZfmr43igJls4oCZ5bCx5Y+v5Lul5Yik5pat6L+U5Zue57uT5p6c6ZuG55qE57G75Z6L44CCDQoNCiMjIyAyLjEgbGFwcGx55L2/55So5pa55byPDQoNCmBsYXBwbHkoWCwgRlVOLCAuLi4pYCBBcHBseSBhIEZ1bmN0aW9uIG92ZXIgYSBMaXN0IG9yIFZlY3RvcihkYXRhLmZyYW1lKSwgaXQgd2lsbCByZXR1cm4gYSBsaXN0IHJhdGhlciB0aGFuIGEgdmVjdG9yLiDlr7nliJfooajlupTnlKjlh73mlbDvvIzov5Tlm57liJfooaguDQoNCuWvueWIl+ihqOaIluiAheWQkemHj+S9v+eUqOWHveaVsCzpgJrov4flr7l455qE5q+P5LiA5Liq5YWD57Sg6L+Q55So5Ye95pWw77yM55Sf5oiQ5LiA5Liq5LiO5YWD57Sg5Liq5pWw55u45ZCM55qE5YC85YiX6KGo44CCbGFwcGx555qE6L+U5Zue5YC85piv5ZKM5LiA5Liq5ZKMWOacieebuOWQjOeahOmVv+W6pueahGxpc3Tlr7nosaHjgILov5nkuKpsaXN05a+56LGh5Lit55qE5q+P5Liq5YWD57Sg5piv5bCG5Ye95pWwRlVO5bqU55So5YiwWOeahOavj+S4gOS4quWFg+e0oO+8jFjkuLpMaXN05a+56LGh77yI6K+lbGlzdOeahOavj+S4quWFg+e0oOmDveaYr+S4gOS4quWQkemHj++8ie+8jOihqOekuuS4gOS4quWQkemHj+aIluiAheihqOi+vuW8j+Wvueixoe+8jOWFtuS7luexu+Wei+eahOWvueixoeS8muiiq1LpgJrov4flh73mlbBgYXMubGlzdCgpYOW8uuWItui9rOaNouS4umxpc3TnsbvlnovjgIIgDQoNCuWHveaVsGxhcHBseeaYr3NhcHBseeWHveaVsOeahOS4gOS4queJueauiuaDheW9ou+8jOWvueS4gOS6m+WPguaVsOeahOWAvOi/m+ihjOS6huS4gOS6m+mZkOWumu+8jOWFtuS9v+eUqOagvOW8j+S4uu+8mmBzYXBwbHkoWCwgRlVOLOKApiwgc2ltcGxpZnkgPSBUUlVFLCBVU0UuTkFNRVMgPSBUUlVFKSBgDQoNCmBzYXBwbHkoLCBzaW1wbGlmeSA9IEZBTFNFLCBVU0UuTkFNRVMgPSBGQUxTRSlgIOWSjGBsYXBwbHkoKWDnmoTov5Tlm57lgLzmmK/nm7jlkIznmoTjgILlpoLmnpzlj4LmlbBgc2ltcGxpZnk9VFJVRWDvvIzliJnlh73mlbBgc2FwcGx5YOeahOi/lOWbnuWAvOS4jeaYr+S4gOS4qmBsaXN0YO+8jOiAjOaYr+S4gOS4quefqemYte+8m+iLpWBzaW1wbGlmeT1GQUxTRWDvvIzliJnlh73mlbBgc2FwcGx5YOeahOi/lOWbnuWAvOS7jeeEtuaYr+S4gOS4qmBsaXN0YOOAgg0KDQojIyMgMi4yIGxhcHBseeekuuS+i+S7o+eggQ0KDQpgYGB7ciBsYXBwbHkgZXgxLCBlY2hvPVRSVUV9DQojIGNyZWF0ZSBsaXN0DQpkYXRhX2xpc3QgPC0gbGlzdChhID0gMToxMCwgDQogICAgICAgICAgIGJldGEgPSBleHAoLTM6MyksIA0KICAgICAgICAgICBsb2dpYyA9IGMoVFJVRSxGQUxTRSxGQUxTRSxUUlVFKSkNCmRhdGFfbGlzdA0KIyAkYQ0KIyAgWzFdICAxICAyICAzICA0ICA1ICA2ICA3ICA4ICA5IDEwDQojIA0KIyAkYmV0YQ0KIyBbMV0gIDAuMDQ5Nzg3MDcgIDAuMTM1MzM1MjggIDAuMzY3ODc5NDQgIDEuMDAwMDAwMDAgIDIuNzE4MjgxODMgIDcuMzg5MDU2MTANCiMgWzddIDIwLjA4NTUzNjkyDQojIA0KIyAkbG9naWMNCiMgWzFdICBUUlVFIEZBTFNFIEZBTFNFICBUUlVFDQoNCmxhcHBseShkYXRhX2xpc3QsIG1lYW4pDQojICRhDQojIFsxXSA1LjUNCiMgDQojICRiZXRhDQojIFsxXSA0LjUzNTEyNQ0KIyANCiMgJGxvZ2ljDQojIFsxXSAwLjUNCg0KbGFwcGx5KGRhdGFfbGlzdCwgcXVhbnRpbGUpDQojICRhDQojICAgIDAlICAgMjUlICAgNTAlICAgNzUlICAxMDAlIA0KIyAgMS4wMCAgMy4yNSAgNS41MCAgNy43NSAxMC4wMCANCiMgDQojICRiZXRhDQojICAgICAgICAgIDAlICAgICAgICAgMjUlICAgICAgICAgNTAlICAgICAgICAgNzUlICAgICAgICAxMDAlIA0KIyAgMC4wNDk3ODcwNyAgMC4yNTE2MDczNiAgMS4wMDAwMDAwMCAgNS4wNTM2Njg5NiAyMC4wODU1MzY5MiANCiMgDQojICRsb2dpYw0KIyAgIDAlICAyNSUgIDUwJSAgNzUlIDEwMCUgDQojICAwLjAgIDAuMCAgMC41ICAxLjAgIDEuMCANCiAgDQpsYXBwbHkoMTozLCBmdW5jdGlvbih4KSB4XjIpDQojW1sxXV0NCiNbMV0gMQ0KIw0KI1tbMl1dDQojWzFdIDQNCiMNCiNbWzNdXQ0KI1sxXSA5DQoNCiMgQW5kIHlvdSBjYW4gdXNlIHVubGlzdCB3aXRoIGxhcHBseSB0byBnZXQgYSB2ZWN0b3IuDQp1bmxpc3QobGFwcGx5KDE6MywgZnVuY3Rpb24oeCkgeF4yKSkNCiNbMV0gMSA0IDkNCg0KDQoj56S65L6L5Luj56CBLOiuoeeul2xpc3TkuK3nmoTmr4/kuKpLRVnlr7nlupTor6XnmoTmlbDmja7nmoTliIbkvY3mlbDjgIINCg0KIyDmnoTlu7rkuIDkuKpsaXN05pWw5o2u6ZuGeO+8jOWIhuWIq+WMheaLrGEsYixjIOS4ieS4qktFWeWAvOOAgg0KeCA8LSBsaXN0KGEgPSAxOjEwLCANCiAgICAgICAgICBiZXRhID0gZXhwKC0zOjMpLCANCiAgICAgICAgICBsb2dpYyA9IGMoVFJVRSxGQUxTRSxGQUxTRSxUUlVFKSk7eA0KIyAkYQ0KIyAgWzFdICAxICAyICAzICA0ICA1ICA2ICA3ICA4ICA5IDEwDQojIA0KIyAkYmV0YQ0KIyBbMV0gIDAuMDQ5Nzg3MDcgIDAuMTM1MzM1MjggIDAuMzY3ODc5NDQgIDEuMDAwMDAwMDAgIDIuNzE4MjgxODMNCiMgWzZdICA3LjM4OTA1NjEwIDIwLjA4NTUzNjkyDQojIA0KIyAkbG9naWMNCiMgWzFdICBUUlVFIEZBTFNFIEZBTFNFICBUUlVFDQoNCiMg5YiG5Yir6K6h566X5q+P5LiqS0VZ5a+55bqU6K+l55qE5pWw5o2u55qE5YiG5L2N5pWw44CCDQpsYXBwbHkoeCwgcXVhbnRpbGUpDQojICRhDQojICAgIDAlICAgMjUlICAgNTAlICAgNzUlICAxMDAlIA0KIyAgMS4wMCAgMy4yNSAgNS41MCAgNy43NSAxMC4wMCANCiMgDQojICRiZXRhDQojICAgICAgICAgIDAlICAgICAgICAgMjUlICAgICAgICAgNTAlICAgICAgICAgNzUlICAgICAgICAxMDAlIA0KIyAgMC4wNDk3ODcwNyAgMC4yNTE2MDczNiAgMS4wMDAwMDAwMCAgNS4wNTM2Njg5NiAyMC4wODU1MzY5MiANCiMgDQojICRsb2dpYw0KIyAgIDAlICAyNSUgIDUwJSAgNzUlIDEwMCUgDQojICAwLjAgIDAuMCAgMC41ICAxLjAgIDEuMCANCg0KYGBgDQoNCmFwcGx55bCx5Y+v5Lul5b6I5pa55L6/5Zyw5oqKbGlzdOaVsOaNrumbhui/m+ihjOW+queOr+aTjeS9nOS6hu+8jOi/mOWPr+S7peeUqGRhdGEuZnJhbWXmlbDmja7pm4bmjInliJfov5vooYzlvqrnjq/vvIzkvYblpoLmnpzkvKDlhaXnmoTmlbDmja7pm4bmmK/kuIDkuKrlkJHph4/miJbnn6npmLXlr7nosaHvvIzpgqPkuYjnm7TmjqXkvb/nlKhsYXBwbHnlsLHkuI3og73ovr7liLDmg7PopoHnmoTmlYjmnpzkuoYNCg0K5q+U5aaC77yM5a+555+p6Zi155qE5YiX5rGC5ZKM44CCDQpgYGB7ciBsYXBwbHkgZXgyLCBlY2hvPVRSVUV9DQojIOeUn+aIkOS4gOS4quefqemYtQ0KeCA8LSBjYmluZCh4MT0zLCB4Mj1jKDI6MSw0OjUpKQ0KeDsgY2xhc3MoeCkNCiMgICAgICB4MSB4Mg0KIyBbMSxdICAzICAyDQojIFsyLF0gIDMgIDENCiMgWzMsXSAgMyAgNA0KIyBbNCxdICAzICA1DQojIFsxXSAibWF0cml4Ig0KDQojIOaxguWSjA0KbGFwcGx5KHgsIHN1bSkNCiMgW1sxXV0NCiMgWzFdIDMNCiMgDQojIFtbMl1dDQojIFsxXSAzDQojIA0KIyBbWzNdXQ0KIyBbMV0gMw0KIyANCiMgW1s0XV0NCiMgWzFdIDMNCiMgDQojIFtbNV1dDQojIFsxXSAyDQojIA0KIyBbWzZdXQ0KIyBbMV0gMQ0KIyANCiMgW1s3XV0NCiMgWzFdIDQNCiMgDQojIFtbOF1dDQojIFsxXSA1DQoNCiMgbGFwcGx55Lya5YiG5Yir5b6q546v55+p6Zi15Lit55qE5q+P5Liq5YC877yM6ICM5LiN5piv5oyJ6KGM5oiW5oyJ5YiX6L+b6KGM5YiG57uE6K6h566X44CCDQoNCiMg5aaC5p6c5a+55pWw5o2u5qGG55qE5YiX5rGC5ZKM44CCbGFwcGx55Lya6Ieq5Yqo5oqK5pWw5o2u5qGG5oyJ5YiX6L+b6KGM5YiG57uE77yM5YaN6L+b6KGM6K6h566X44CCDQpsYXBwbHkoZGF0YS5mcmFtZSh4KSwgc3VtKQ0KIyAkeDENCiMgWzFdIDEyDQojIA0KIyAkeDINCiMgWzFdIDEyDQpgYGANCg0KDQoNCg0KIyMgMy4gc2FwcGx5DQoNCnNhcHBseeWHveaVsOaYr+S4gOS4queugOWMlueJiOeahGxhcHBsee+8jHNhcHBseeWinuWKoOS6hjLkuKrlj4LmlbBzaW1wbGlmeeWSjFVTRS5OQU1FU++8jOS4u+imgeWwseaYr+iuqei+k+WHuueci+i1t+adpeabtOWPi+Wlve+8jOi/lOWbnuWAvOS4uuWQkemHj++8jOiAjOS4jeaYr2xpc3Tlr7nosaHjgIINCg0KIyMjIDMuMSBzYXBwbHnkvb/nlKjmlrnlvI8NCg0KYHNhcHBseShYLCBGVU4sIC4uLiwgc2ltcGxpZnkgPSBUUlVFLCBVU0UuTkFNRVMgPSBUUlVFKWAgQXBwbHkgYSBGdW5jdGlvbiBvdmVyIGEgTGlzdCBvciBWZWN0b3IsIOWvueaVsOe7hOOAgeefqemYteOAgeaVsOaNruahhuS9v+eUqOWHveaVsCwg6L+U5Zue5ZCR6YeP77yM562J5Lu35LqOYHVubGlzdChsYXBwbHko4oCmKSlg77yM55SoYGxhcHBseWDorqHnrpfvvIznhLblkI7miornu5Pmnpzlj5jkuLrlkJHph4/jgIINCg0K6L+Z5piv5LiA5Liq55So5oi35Y+L5aW954mI5pys77yM5pivbGFwcGx55Ye95pWw55qE5YyF6KOF54mI44CC6K+l5Ye95pWw6L+U5Zue5YC85Li65ZCR6YeP44CB55+p6Zi177yM5aaC5p6cYHNpbXBsaWZ5PSJhcnJheSJg77yM5LiU5ZCI6YCC55qE5oOF5Ya15LiL77yM5bCG5Lya6YCa6L+HYHNpbXBsaWZ5MmFycmF5KClg5Ye95pWw6L2s5o2i5Li655+p6Zi144CCYHNhcHBseSh4LCBmLCBzaW1wbGlmeT1GQUxTRSwgVVNFLk5BTUVTPUZBTFNFKWDov5Tlm57nmoTlgLzkuI5gbGFwcGx5KHgsZilg5piv5LiA6Ie055qE44CCDQoNCljooajnpLrkuIDkuKrlkJHph4/miJbogIXooajovr7lvI/lr7nosaHvvIzlhbbkvZnlr7nosaHlsIbooqvpgJrov4dgYXMubGlzdGDlvLrliLbovazmjaLkuLpgbGlzdGDjgIINCmBzaW1wbGlmeWAg6YC76L6R5YC85oiW6ICF5a2X56ym5Liy77yM5aaC5p6c5Y+v5Lul77yM57uT5p6c5bqU6K+l6KKr566A5YyW5Li65ZCR6YeP44CB55+p6Zi15oiW6ICF6auY57u05pWw57uE44CC5b+F6aG75piv5ZG95ZCN55qE77yM5LiN6IO95piv566A5YaZ44CC6buY6K6k5YC85pivVFJVRe+8jOiLpeWQiOmAguWwhuS8mui/lOWbnuS4gOS4quWQkemHj+aIluiAheefqemYteOAguWmguaenGBzaW1wbGlmeT0iYXJyYXkiYO+8jOe7k+aenOWwhui/lOWbnuS4gOS4quefqemYtSBhcnJheS9tYXRyaXgs6L6T5Ye657uT5p6c5oyJ5pWw57uE6L+b6KGM5YiG57uE44CCDQpgVVNFLk5BTUVTYOmAu+i+keWAvO+8jOWmguaenOS4umBUUlVFYO+8jOS4lHjmsqHmnInooqvlkb3lkI3vvIzliJnlr7l46L+b6KGM5ZG95ZCN44CC5aaC5p6cWOS4uuWtl+espuS4su+8jFRSVUXorr7nva7lrZfnrKbkuLLkuLrmlbDmja7lkI3vvIxGQUxTReS4jeiuvue9rg0KDQojIyMgMy4yIHNhcHBseeekuuS+i+S7o+eggQ0KYGBge3Igc2FwcGx5IGV4MSwgZWNobz1UUlVFfQ0KIyBzaG93IGNvZGUgYW5kIG91dHB1dA0Kc2FwcGx5KDE6MywgZnVuY3Rpb24oeCkgeF4yKQ0KI1sxXSAxIDQgOQ0KDQpgYGANCg0KUGFzc2luZyBgc2ltcGxpZnk9RkFMU0VgIHRvIHNhcHBseSB3aWxsIGFsc28gZ2l2ZSB5b3UgYSBsaXN0Og0KDQpgYGB7ciBzYXBwbHkgZXgyLCBlY2hvPVRSVUV9DQojIHNob3cgY29kZSBhbmQgb3V0cHV0DQpzYXBwbHkoMTozLCBmdW5jdGlvbih4KSB4XjIsIHNpbXBsaWZ5ID0gRikNCiNbWzFdXQ0KI1sxXSAxDQojDQojW1syXV0NCiNbMV0gNA0KIw0KI1tbM11dDQojWzFdIDkNCmBgYA0KDQoNCiMjIDQuIHZhcHBseQ0KQXBwbHkgYSBGdW5jdGlvbiBvdmVyIGEgTGlzdCBvciBWZWN0b3INCuWvueWIl+ihqOaIluiAheWQkemHj+S9v+eUqOWHveaVsA0KdmFwcGx5KFgsIEZVTiwgRlVOLlZBTFVFLCAuLi4sIFVTRS5OQU1FUyA9IFRSVUUpDQojIyMg5L2/55So5pa55byPDQojIyMg56S65L6L5Luj56CBDQoNCiMjIDUuIHRhcHBseQ0KDQojIyMgNS4xIHRhcHBseSDkvb/nlKjmlrnlvI8NCmB0YXBwbHkoWCwgSU5ERVgsIEZVTiA9IE5VTEwsIC4uLiwgc2ltcGxpZnkgPSBUUlVFKWAgQXBwbHkgYSBGdW5jdGlvbiBPdmVyIGEgUmFnZ2VkIEFycmF5IOWvueS4jeinhOWImemYteWIl+S9v+eUqOWHveaVsC4NCg0K5YW25LitYFhg6YCa5bi45piv5LiA5ZCR6YeP77ybDQpgSU5ERVhg5piv5LiA5LiqbGlzdOWvueixoe+8jOS4lOivpWxpc3TkuK3nmoTmr4/kuIDkuKrlhYPntKDpg73mmK/kuI5Y5pyJ5ZCM5qC36ZW/5bqm55qE5Zug5a2Q77ybDQpgRlVOYOaYr+mcgOimgeiuoeeul+eahOWHveaVsO+8mw0KYHNpbXBsaWZ5YOaYr+mAu+i+keWPmOmHj++8jOiLpeWPluWAvOS4ulRSVUXvvIjpu5jorqTlgLzvvInvvIzkuJTlh73mlbBgRlVOYOeahOiuoeeul+e7k+aenOaAu+aYr+S4uuS4gOS4quagh+mHj+WAvO+8jOmCo+S5iOWHveaVsGB0YXBwbHlg6L+U5Zue5LiA5Liq5pWw57uE77yb6Iul5Y+W5YC85Li6YEZBTFNFYO+8jOWImeWHveaVsGB0YXBwbHlg55qE6L+U5Zue5YC85Li65LiA5LiqbGlzdOWvueixoeOAgg0KDQrpnIDopoHms6jmhI/nmoTmmK/vvIzlvZPnrKzkuozkuKrlj4LmlbBgSU5ERVhg5LiN5piv5Zug5a2Q5pe277yM5Ye95pWwYHRhcHBseSgpYOWQjOagt+acieaViO+8jOWboOS4uuW/heimgeaXtiBSIOS8mueUqGBhcy5mYWN0b3IoKWDmiorlj4LmlbDlvLrliLbovazmjaLmiJDlm6DlrZDjgIIqKuS4gOiIrOeUqOS6jmZhY3Rvcu+8jOWIqeeUqGB0YXBwbHlg5Y+v5Lul5a6e546wZXhjZWzmlbDmja7pgI/op4booajnmoTlip/og70qKi4NCg0KIyMjIDUuMiB0YXBwbHkg56S65L6L5Luj56CBDQpgYGB7ciB0YXBwbHkgZXgxLCBlY2hvPVRSVUV9DQpoZWlnaHQgPC0gYygxNzQsIDE2NSwgMTgwLCAxNzEsIDE2MCkNCnNleDwtYygiRiIsIkYiLCJNIiwiRiIsIk0iKQ0KDQp0YXBwbHkoaGVpZ2h0LCBzZXgsIG1lYW4pDQojICAgRiAgIE0gDQojIDE3MCAxNzAgDQpgYGANCg0KYGBge3IgdGFwcGx5IGV4MiwgZWNobz1UUlVFfQ0KIyB0YXBwbHkgZXggMg0KbiA8LSAxNzsgZmFjIDwtIGZhY3RvcihyZXAoMTozLCBsZW5ndGggPSBuKSwgbGV2ZWxzID0gMTo1KTtmYWMNCiAjIFsxXSAxIDIgMyAxIDIgMyAxIDIgMyAxIDIgMyAxIDIgMyAxIDINCiMgTGV2ZWxzOiAxIDIgMyA0IDUNCnRhYmxlKGZhYykNCiMgZmFjDQojIDEgMiAzIDQgNSANCiMgNiA2IDUgMCAwIA0KDQp0YXBwbHkoMTpuLCBmYWMsIHN1bSkNCiAjIDEgIDIgIDMgIDQgIDUgDQojIDUxIDU3IDQ1IE5BIE5BIA0KDQp0YXBwbHkoMTpuLCBmYWMsIHN1bSwgc2ltcGxpZnkgPSBGQUxTRSkNCiMgJGAxYA0KIyBbMV0gNTENCiMgDQojICRgMmANCiMgWzFdIDU3DQojIA0KIyAkYDNgDQojIFsxXSA0NQ0KIyANCiMgJGA0YA0KIyBOVUxMDQojIA0KIyAkYDVgDQojIE5VTEwNCg0KdGFwcGx5KDE6biwgZmFjLCByYW5nZSkNCiMgJGAxYA0KIyBbMV0gIDEgMTYNCiMgDQojICRgMmANCiMgWzFdICAyIDE3DQojIA0KIyAkYDNgDQojIFsxXSAgMyAxNQ0KIyANCiMgJGA0YA0KIyBOVUxMDQojIA0KIyAkYDVgDQojIE5VTEwNCg0KdGFwcGx5KDE6biwgZmFjLCBxdWFudGlsZSkNCiMgJGAxYA0KIyAgICAwJSAgIDI1JSAgIDUwJSAgIDc1JSAgMTAwJSANCiMgIDEuMDAgIDQuNzUgIDguNTAgMTIuMjUgMTYuMDAgDQojIA0KIyAkYDJgDQojICAgIDAlICAgMjUlICAgNTAlICAgNzUlICAxMDAlIA0KIyAgMi4wMCAgNS43NSAgOS41MCAxMy4yNSAxNy4wMCANCiMgDQojICRgM2ANCiMgICAwJSAgMjUlICA1MCUgIDc1JSAxMDAlIA0KIyAgICAzICAgIDYgICAgOSAgIDEyICAgMTUgDQojIA0KIyAkYDRgDQojIE5VTEwNCiMgDQojICRgNWANCiMgTlVMTA0KYGBgDQoNCg0KIyMgNi4gZWFwcGx5DQoNCuWvueS4gOS4queOr+Wig+epuumXtOS4reeahOaJgOacieWPmOmHj+i/m+ihjOmBjeWOhuOAguWmguaenOaIkeS7rOacieWlveeahOS5oOaDr++8jOaKiuiHquWumuS5ieeahOWPmOmHj+mDveaMieS4gOWumueahOinhOWImeWtmOWCqOWIsOiHquWumuS5ieeahOeOr+Wig+epuumXtOS4re+8jOmCo+S5iOi/meS4quWHveaVsOWwhuS8muiuqeS9oOeahOaTjeS9nOWPmOW+l+mdnuW4uOaWueS+v+OAguW9k+eEtu+8jOWPr+iDveW+iOWkmuS6uumDveS4jeeGn+aCieepuumXtOeahOaTjeS9nO+8jOmCo+S5iOivt+WPguiAg+aWh+eroCBb5o+t5byAUuivreiogOS4reeOr+Wig+epuumXtOeahOelnuenmOmdoue6sV0oaHR0cDovL2Jsb2cuZmVucy5tZS9yLWVudmlyb25tZW50cy8p77yMW+ino+WvhlLor63oqIDlh73mlbDnmoTnjq/looPnqbrpl7RdKGh0dHA6Ly9ibG9nLmZlbnMubWUvci1lbnZpcm9ubWVudHMtZnVuY3Rpb24vKeOAgg0KDQojIyMgNi4xIGVhcHBseeS9v+eUqOaWueW8jw0KYGVhcHBseShlbnYsIEZVTiwgLi4uLCBhbGwubmFtZXMgPSBGQUxTRSwgVVNFLk5BTUVTID0gVFJVRSlgLEFwcGx5IGEgRnVuY3Rpb24gT3ZlciBWYWx1ZXMgaW4gYW4gRW52aXJvbm1lbnTlr7nnjq/looPkuK3nmoTlgLzkvb/nlKjlh73mlbDjgIINCg0KZWFwcGx55Ye95pWw6YCa6L+H5a+5ZW52aXJvbm1lbnTkuK3lkb3lkI3lgLzov5vooYxGVU7orqHnrpflkI7ov5Tlm57kuIDkuKrliJfooajlgLzvvIznlKjmiLflj6/ku6Xor7fmsYLmiYDmnInkvb/nlKjov4fnmoTlkb3lkI3lr7nosaHjgIJgZW52YOWwhuiiq+S9v+eUqOeahOeOr+Wig++8jGBhbGwubmFtZXNgICDpgLvovpHlgLzvvIzmjIfnpLrmmK/lkKblr7nmiYDmnInlgLzkvb/nlKjor6Xlh73mlbDvvIwgYFVTRS5OQU1FU2AgIOmAu+i+keWAvO+8jOaMh+ekuui/lOWbnueahOWIl+ihqOe7k+aenOaYr+WQpuWMheWQq+WRveWQjQ0KDQojIyMgNi4yIGVhcHBseeekuuS+i+S7o+eggQ0KDQpgYGB7ciBlYXBwbHkgZXgxfQ0KcmVxdWlyZShzdGF0cykNCg0KIyDlrprkuYnkuIDkuKrnjq/looPnqbrpl7QNCmVudiA8LSBuZXcuZW52KGhhc2ggPSBGQUxTRSkgIyBzbyB0aGUgb3JkZXIgaXMgZml4ZWQNCg0KIyDlkJHov5nkuKrnjq/looPnqbrpl7TkuK3lrZjlhaUz5Liq5Y+Y6YePDQplbnYkYSA8LSAxOjEwDQplbnYkYmV0YSA8LSBleHAoLTM6MykNCmVudiRsb2dpYyA8LSBjKFRSVUUsIEZBTFNFLCBGQUxTRSwgVFJVRSkNCg0KIyB3aGF0IGhhdmUgd2UgdGhlcmU/5p+l55yLZW5256m66Ze05Lit55qE5Y+Y6YePDQpscyhlbnYpDQoNCiMg5p+l55yLZW5256m66Ze05Lit55qE5Y+Y6YeP5a2X56ym5Liy57uT5p6EDQpscy5zdHIoZW52KQ0KDQp1dGlsczo6bHMuc3RyKGVudikNCiMgYSA6ICBpbnQgWzE6MTBdIDEgMiAzIDQgNSA2IDcgOCA5IDEwDQojIGJldGEgOiAgbnVtIFsxOjddIDAuMDQ5OCAwLjEzNTMgMC4zNjc5IDEgMi43MTgzIC4uLg0KIyBsb2dpYyA6ICBsb2dpIFsxOjRdIFRSVUUgRkFMU0UgRkFMU0UgVFJVRQ0KDQojIGNvbXB1dGUgdGhlIG1lYW4gZm9yIGVhY2ggbGlzdCBlbGVtZW50DQplYXBwbHkoZW52LCBtZWFuKQ0KIyAkbG9naWMNCiMgWzFdIDAuNQ0KIyANCiMgJGJldGENCiMgWzFdIDQuNTM1MTI1DQojIA0KIyAkYQ0KIyBbMV0gNS41DQoNCnVubGlzdChlYXBwbHkoZW52LCBtZWFuLCBVU0UuTkFNRVMgPSBGQUxTRSkpDQojIFsxXSAwLjUwMDAwMCA0LjUzNTEyNSA1LjUwMDAwMA0KDQojIG1lZGlhbiBhbmQgcXVhcnRpbGVzIGZvciBlYWNoIGVsZW1lbnQgKG1ha2luZyB1c2Ugb2YgIi4uLiIgcGFzc2luZyk6DQplYXBwbHkoZW52LCBxdWFudGlsZSwgcHJvYnMgPSAxOjMvNCkNCiMgJGxvZ2ljDQojIDI1JSA1MCUgNzUlIA0KIyAwLjAgMC41IDEuMCANCiMgDQojICRiZXRhDQojICAgICAgIDI1JSAgICAgICA1MCUgICAgICAgNzUlIA0KIyAwLjI1MTYwNzQgMS4wMDAwMDAwIDUuMDUzNjY5MCANCiMgDQojICRhDQojICAyNSUgIDUwJSAgNzUlIA0KIyAzLjI1IDUuNTAgNy43NSANCg0KZWFwcGx5KGVudiwgcXVhbnRpbGUpDQojICRsb2dpYw0KIyAgIDAlICAyNSUgIDUwJSAgNzUlIDEwMCUgDQojICAwLjAgIDAuMCAgMC41ICAxLjAgIDEuMCANCiMgDQojICRiZXRhDQojICAgICAgICAgIDAlICAgICAgICAgMjUlICAgICAgICAgNTAlICAgICAgICAgNzUlICAgICAgICAxMDAlIA0KIyAgMC4wNDk3ODcwNyAgMC4yNTE2MDczNiAgMS4wMDAwMDAwMCAgNS4wNTM2Njg5NiAyMC4wODU1MzY5MiANCiMgDQojICRhDQojICAgIDAlICAgMjUlICAgNTAlICAgNzUlICAxMDAlIA0KIyAgMS4wMCAgMy4yNSAgNS41MCAgNy43NSAxMC4wMCANCg0KIyDlho3orqHnrpfkuK3lvZPliY3njq/looPnqbrpl7TkuK3nmoTmiYDmnInlj5jph4/nmoTljaDnlKjlhoXlrZjlpKflsI/jgIINCiMg5p+l55yL5b2T5YmN546v5aKD56m66Ze05Lit55qE5Y+Y6YePDQpscygpDQoNCiMg5p+l55yL5omA5pyJ5Y+Y6YeP55qE5Y2g55So5YaF5a2Y5aSn5bCPDQplYXBwbHkoZW52aXJvbm1lbnQoKSwgb2JqZWN0LnNpemUpDQoNCmBgYA0KDQplYXBwbHnlh73mlbDlubPml7blvojpmr7ooqvnlKjliLDvvIzkvYblr7nkuo5S5YyF5byA5Y+R5p2l6K+077yM546v5aKD56m66Ze055qE5L2/55So5piv5b+F6aG76KaB5o6M5o+h55qE44CC54m55Yir5piv5b2TUuimgeWBmuS4uuW3peS4muWMlueahOW3peWFt+aXtu+8jOWvueWPmOmHj+eahOeyvuehruaOp+WItuWSjOeuoeeQhuaYr+mdnuW4uOW/heimgeeahOOAgg0KDQojIyA3LiBtYXBwbHkNCg0KIyMjIDcuMSBtYXBwbHnkvb/nlKjmlrnlvI8NCg0KYG1hcHBseShGVU4sIC4uLiwgTW9yZUFyZ3MgPSBOVUxMLCBTSU1QTElGWSA9IFRSVUUsIFVTRS5OQU1FUyA9IFRSVUUpYCwgQXBwbHkgYSBGdW5jdGlvbiB0byBNdWx0aXBsZSBMaXN0IG9yIFZlY3RvciBBcmd1bWVudHMu5a+55aSa5Liq5YiX6KGo5oiW6ICF5ZCR6YeP5Y+C5pWw5L2/55So5Ye95pWwDQoNCm1hcHBseeaYr3NhcHBseeeahOWkmuWPmOmHj+eJiOacrOOAguWwhuWvuS4uLuS4reeahOavj+S4quWPguaVsOi/kOihjEZVTuWHveaVsO+8jOWmguacieW/heimge+8jOWPguaVsOWwhuiiq+W+queOr+OAgg0KDQpgTW9yZUFyZ3NgIEZVTuWHveaVsOeahOWFtuS7luWPguaVsOWIl+ihqA0KYFNJTVBMSUZZYCDpgLvovpHmiJbogIXlrZfnrKbkuLLvvIzlj6/ku6Xlh4/lsJHnu5PmnpzmiJDkuLrkuIDkuKrlkJHph4/jgIHnn6npmLXmiJbogIXmm7Tpq5jnu7TpmLXliJfvvIzor6bop4FzYXBwbHnnmoRzaW1wbGlmeeWPguaVsA0KYFVTRS5OQU1FU2DpgLvovpHlgLzvvIzlpoLmnpznrKzkuIDkuKrlj4LmlbAuLi7lt7Looqvlkb3lkI3vvIzlsIbkvb/nlKjov5nkuKrlrZfnrKblkJHph4/kvZzkuLrlkI3lrZcNCg0KIyMjIDcuMiDnpLrkvovku6PnoIENCmBgYHtyIG1hcHBseSBleDF9DQptYXBwbHkocmVwLCAxOjQsIDQ6MSkNCiMgW1sxXV0NCiMgWzFdIDEgMSAxIDENCiMgDQojIFtbMl1dDQojIFsxXSAyIDIgMg0KIyANCiMgW1szXV0NCiMgWzFdIDMgMw0KIyANCiMgW1s0XV0NCiMgWzFdIDQNCg0KbWFwcGx5KGZ1bmN0aW9uKHgsIHkpIHNlcV9sZW4oeCkgKyB5LA0KICAgICAgIGMoYSA9ICAxLCBiID0gMiwgYyA9IDMpLCAgIyBuYW1lcyBmcm9tIGZpcnN0DQogICAgICAgYyhBID0gMTAsIEIgPSAwLCBDID0gLTEwKSkNCiMgJGENCiMgWzFdIDExDQojIA0KIyAkYg0KIyBbMV0gMSAyDQojIA0KIyAkYw0KIyBbMV0gLTkgLTggLTcNCmBgYA0KDQoNCg0KDQojIyA4LiByYXBwbHkNCg0KIyMjIDguMSByYXBwbHnkvb/nlKjmlrnlvI8NCmByYXBwbHkoWCwgRlVOLCBjbGFzc2VzID0gIkFOWSIsIGRlZmx0ID0gTlVMTCwgaG93ID0gYygidW5saXN0IiwgInJlcGxhY2UiLCAibGlzdCIpLCAuLi4pYCDov5DnlKjlh73mlbDpgJLlvZLkuqfnlJ/liJfooagscmFwcGx55pivbGFwcGx555qE6YCS5b2S54mI5pysLlJlY3Vyc2l2ZWx5IEFwcGx5IGEgRnVuY3Rpb24gdG8gYSBMaXN0Lg0KWCAg5LiA5Liq5YiX6KGoDQpjbGFzc2VzICDlhbPkuo7nsbvlkI3nmoTlrZfnrKblkJHph4/vvIzmiJbogIXkuLphbnnml7bliJnljLnphY3ku7vkvZXnsbsNCmRlZmx0ICDpu5jorqTnu5PmnpzvvIzlpoLmnpzkvb/nlKjkuoZob3c94oCdcmVwbGFjZeKAne+8jOWImeS4jeiDveS9v+eUqA0KaG93ICDlrZfnrKbkuLLljLnphY3kuInnp43lj6/og73nu5PmnpwNCg0KIyMjIDguMiByYXBwbHnnpLrkvovku6PnoIENCg0KDQoNCkhvd2V2ZXIgdGhlIGJlaHZpb3VyIGlzIG5vdCBhcyBjbGVhbiB3aGVuIHRoaW5ncyBoYXZlIG5hbWVzLCBzbyBiZXN0IHRvIHVzZSBzYXBwbHkgb3IgbGFwcGx5IGFzIG1ha2VzIHNlbnNlIGZvciB5b3VyIGRhdGEgYW5kIHdoYXQgeW91IHdhbnQgdG8gcmVjZWl2ZSBiYWNrLiBJZiB5b3Ugd2FudCBhIGxpc3QgcmV0dXJuZWQsIHVzZSBsYXBwbHkuIElmIHlvdSB3YW50IGEgdmVjdG9yLCB1c2Ugc2FwcGx5Lg0KRGlydHkgRGVlZHMNCg0KQW55d2F5LCBhIGNoZWFwIHRyaWNrIGlzIHRvIHBhc3Mgc2FwcGx5IGEgdmVjdG9yIG9mIGluZGV4ZXMgYW5kIHdyaXRlIHlvdXIgZnVuY3Rpb24gbWFraW5nIHNvbWUgYXNzdW1wdGlvbnMgYWJvdXQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgdW5kZXJseWluZyBkYXRhLiBMZXTigJlzIGxvb2sgYXQgb3VyIG1lYW4gZXhhbXBsZSBhZ2FpbjoNCg0KYGBge3Igc2FwcGx5MywgZWNobz1UUlVFfQ0KIyBzaG93IGNvZGUgYW5kIG91dHB1dA0Kc2FwcGx5KDE6MywgZnVuY3Rpb24oeCkgbWVhbihtWyx4XSkpDQojWzFdIC0wLjAyNjY0NDE4ICAxLjk1ODEyNDU4ICA0Ljg2ODU3NzkyDQpgYGANCg0KV2UgcGFzcyB0aGUgY29sdW1uIGluZGV4ZXMgKDEsMiwzKSB0byBvdXIgZnVuY3Rpb24sIHdoaWNoIGFzc3VtZXMgc29tZSB2YXJpYWJsZSBtIGhhcyBvdXIgZGF0YS4gRmluZSBmb3IgcXVpY2tpZXMgYnV0IG5vdCB2ZXJ5IG5pY2UsIGFuZCB3aWxsIGxpa2VseSB0dXJuIGludG8gYSBtYWludGFpbmFiaWxpdHkgYm9tYiBkb3duIHRoZSBsaW5lLg0KV2UgY2FuIG5lYXRlbiB0aGluZ3MgdXAgYSBiaXQgYnkgcGFzc2luZyBvdXIgZGF0YSBpbiBhbiBhcmd1bWVudCB0byBvdXIgZnVuY3Rpb24sIGFuZCB1c2luZyB0aGUg4oCmIHNwZWNpYWwgYXJndW1lbnQgd2hpY2ggYWxsIHRoZSBhcHBseSBmdW5jdGlvbnMgaGF2ZSBmb3IgcGFzc2luZyBleHRyYSBhcmd1bWVudHM6DQoNCmBgYHtyIHNhcHBseTQsIGVjaG89VFJVRX0NCiMgc2hvdyBjb2RlIGFuZCBvdXRwdXQNCnNhcHBseSgxOjMsIGZ1bmN0aW9uKHgsIHkpIG1lYW4oeVsseF0pLCB5PW0pDQojWzFdIC0wLjAyNjY0NDE4ICAxLjk1ODEyNDU4ICA0Ljg2ODU3NzkyDQpgYGANCg0KVGhpcyB0aW1lLCBvdXIgZnVuY3Rpb24gaGFzIDIgYXJndW1lbnRzLCB4IGFuZCB5LiBUaGUgeCB2YXJpYWJsZSB3aWxsIGJlIGFzIGl0IHdhcyBiZWZvcmUsIHdoYXRldmVyIHNhcHBseSBpcyBjdXJyZW50bHkgZ29pbmcgdGhyb3VnaC4gVGhlIHkgdmFyaWFibGUgd2Ugd2lsbCBwYXNzIHVzaW5nIHRoZSBvcHRpb25hbCBhcmd1bWVudHMgdG8gc2FwcGx5Lg0KDQpJbiB0aGlzIGNhc2Ugd2UgaGF2ZSBwYXNzZWQgaW4gbSwgZXhwbGljaXRseSBuYW1pbmcgdGhlIHkgYXJndW1lbnQgaW4gdGhlIHNhcHBseSBjYWxsLiBOb3Qgc3RyaWN0bHkgbmVjZXNzYXJ5IGJ1dCBpdCBtYWtlcyBmb3IgZWFzaWVyIHRvIHJlYWQgJiBtYWludGFpbiBjb2RlLiBUaGUgeSB2YWx1ZSB3aWxsIGJlIHRoZSBzYW1lIGZvciBlYWNoIGNhbGwgc2FwcGx5IG1ha2VzIHRvIG91ciBmdW5jdGlvbi4NCg0KSSBkb27igJl0IHJlYWxseSByZWNvbW1lbmQgcGFzc2luZyB0aGUgaW5kZXggYXJndW1lbnRzIGxpa2UgdGhpcywgaXQgaXMgZXJyb3IgcHJvbmUgYW5kIGNhbiBiZSBxdWl0ZSBjb25mdXNpbmcgdG8gb3RoZXJzIHJlYWRpbmcgeW91ciBjb2RlLg0KDQpJIGhvcGUgeW91IGZvdW5kIHRoZXNlIGV4YW1wbGVzIGhlbHBmdWwuIFBsZWFzZSBjaGVjayBvdXQgcGFydCAyIHdoZXJlIHdlIGNyZWF0ZSBhIGRlbnNpdHkgcGxvdCBvZiB0aGUgdmFsdWVzIGluIG91ciBtYXRyaXguDQoNCiMjIHRhcHBseSBncm91cCBzdW1tYXJ5LS3lh73mlbB0YXBwbHnov5vooYzliIbnu4Tnu5/orqENCg0KYHRhcHBseShYLCBJTkRFWCwgRlVOID0gTlVMTCwgLi4uLCBzaW1wbGlmeSA9IFRSVUUpYA0KDQrlr7nlkITlm6DlrZDlupTnlKjlh73mlbDvvIjkuZ/lsLHmmK/liIbnu4TorqHnrpfvvInvvIzov5nkuKrkuZ/luLjnlKguDQoNCuWFtuS4rVjpgJrluLjmmK/kuIDlkJHph4/vvJtJTkRFWOaYr+S4gOS4qmxpc3Tlr7nosaHvvIzkuJTor6VsaXN05Lit55qE5q+P5LiA5Liq5YWD57Sg6YO95piv5LiOWOacieWQjOagt+mVv+W6pueahOWboOWtkO+8m0ZVTuaYr+mcgOimgeiuoeeul+eahOWHveaVsO+8mw0KDQpzaW1wbGlmeeaYr+mAu+i+keWPmOmHj++8jOiLpeWPluWAvOS4ulRSVUXvvIjpu5jorqTlgLzvvInvvIzkuJTlh73mlbBGVU7nmoTorqHnrpfnu5PmnpzmgLvmmK/kuLrkuIDkuKrmoIfph4/lgLzvvIzpgqPkuYjlh73mlbB0YXBwbHnov5Tlm57kuIDkuKrmlbDnu4TvvJsNCg0K6Iul5Y+W5YC85Li6RkFMU0XvvIzliJnlh73mlbB0YXBwbHnnmoTov5Tlm57lgLzkuLrkuIDkuKpsaXN05a+56LGh44CCDQoNCumcgOimgeazqOaEj+eahOaYr++8jOW9k+esrOS6jOS4quWPguaVsElOREVY5LiN5piv5Zug5a2Q5pe277yM5Ye95pWwIHRhcHBseSgpIOWQjOagt+acieaViO+8jOWboOS4uuW/heimgeaXtiBSIOS8mueUqCBhcy5mYWN0b3IoKeaKiuWPguaVsOW8uuWItui9rOaNouaIkOWboOWtkOOAgg0KDQrnpLrkvovku6PnoIHvvJoNCg0KYGBge3J9DQpmYWMgPC0gZmFjdG9yKHJlcCgxOjUsIGxlbmd0aCA9IDE3KSwgbGV2ZWxzID0gMTo1KTtmYWMNCiAjIFsxXSAxIDIgMyA0IDUgMSAyIDMgNCA1IDEgMiAzIDQgNSAxIDINCiMgTGV2ZWxzOiAxIDIgMyA0IDUNCg0KdGFwcGx5KDE6MTcsIGZhYywgc3VtKQ0KIyAgMSAgMiAgMyAgNCAgNSANCiMgMzQgMzggMjQgMjcgMzAgDQoNCnRhcHBseSgxOjE3LCBmYWMsIHN1bSwgc2ltcGxpZnkgPSBGQUxTRSkNCiMgJGAxYA0KIyBbMV0gMzQNCiMgDQojICRgMmANCiMgWzFdIDM4DQojIA0KIyAkYDNgDQojIFsxXSAyNA0KIyANCiMgJGA0YA0KIyBbMV0gMjcNCiMgDQojICRgNWANCiMgWzFdIDMwDQoNCnRhcHBseSgxOjE3LCBmYWMsIHJhbmdlKQ0KIyAkYDFgDQojIFsxXSAgMSAxNg0KIyANCiMgJGAyYA0KIyBbMV0gIDIgMTcNCiMgDQojICRgM2ANCiMgWzFdICAzIDEzDQojIA0KIyAkYDRgDQojIFsxXSAgNCAxNA0KIyANCiMgJGA1YA0KIyBbMV0gIDUgMTUNCmBgYA0KDQojIyMg5Yip55SodGFwcGx55a6e546w57G75Ly85LqOZXhjZWzph4znmoTmlbDmja7pgI/op4booajnmoTlip/og70NCmBgYHtyIHRhcHBseX0NCnllYXIgPC0gYygyMDA3LCAyMDA3LCAyMDA3LCAyMDA3LCAyMDA4LCAyMDA4LCAyMDA4LCAyMDA5LCAyMDA5LDIwMDkpDQpwcm92aW5jZSA8LSBjKCJBIiwgIkIiLCAiQyIsICJEIiwgIkEiLCAiQyIsICJEIiwgIkIiLCAiQyIsICJEIikNCnNhbGUgPC0gMToxMA0KDQpteWRhdGEgPC0gZGF0YS5mcmFtZSh5ZWFyLCBwcm92aW5jZSwgc2FsZSk7bXlkYXRhDQoNCmF0dGFjaChteWRhdGEpDQoNCnRhcHBseShzYWxlLCBsaXN0KHllYXIsIHByb3ZpbmNlKSkNCiMgWzFdICAxICA0ICA3IDEwICAyICA4IDExICA2ICA5IDEyDQoNCnRhcHBseShzYWxlLGxpc3QoeWVhcixwcm92aW5jZSksbWVhbikNCiMgICAgICAgQSAgQiBDICBEDQojIDIwMDcgIDEgIDIgMyAgNA0KIyAyMDA4ICA1IE5BIDYgIDcNCiMgMjAwOSBOQSAgOCA5IDEwDQoNCmBgYA0KDQojIyDlh73mlbB0YWJsZe+8iOaxguWboOWtkOWHuueOsOeahOmikeaVsO+8ie+8mg0KDQrkvb/nlKjmoLzlvI/kuLrvvJpgdGFibGUoLi4uLCBleGNsdWRlID0gaWYgKHVzZU5BID09ICJubyIpIGMoTkEsIE5hTiksIHVzZU5BID0gYygibm8iLCJpZmFueSIsICJhbHdheXMiKSwgZG5uID0gbGlzdC5uYW1lcyguLi4pLCBkZXBhcnNlLmxldmVsID0gMSlgIOWFtuS4reWPguaVsGV4Y2x1ZGXooajnpLrlk6rkupvlm6DlrZDkuI3orqHnrpfjgIINCg0K56S65L6L5Luj56CB77yaDQpgYGB7cn0NCmQgPC0gZmFjdG9yKHJlcChjKCJBIiwiQiIsIkMiKSwgMTApLCANCiAgICAgICAgICAgIGxldmVscz1jKCJBIiwiQiIsIkMiLCJEIiwiRSIpKTsgZA0KICMgWzFdIEEgQiBDIEEgQiBDIEEgQiBDIEEgQiBDIEEgQiBDIEEgQiBDIEEgQiBDIEEgQiBDIEEgQiBDIEEgQiBDDQojIExldmVsczogQSBCIEMgRCBFDQoNCnRhYmxlKGQpDQojIGQNCiAjIEEgIEIgIEMgIEQgIEUNCiMgMTAgMTAgMTAgIDAgIDANCg0KdGFibGUoZCwgZXhjbHVkZT0iQiIpDQojIGQNCiAjIEEgIEMgIEQgIEUNCiMgMTAgMTAgIDAgIDANCmBgYA0KDQoNCg0KIyMgc2FwcGx5DQoNCuWHveaVsHNhcHBseeaYr+WHveaVsGxhcHBseeeahOS4gOS4queJueauiuaDheW9ou+8jOWvueS4gOS6m+WPguaVsOeahOWAvOi/m+ihjOS6huS4gOS6m+mZkOWumu+8jOWFtuS9v+eUqOagvOW8j+S4uu+8mmBzYXBwbHkoWCwgRlVOLC4uLiwgc2ltcGxpZnkgPSBUUlVFLCBVU0UuTkFNRVMgPSBUUlVFKWANCg0KYHNhcHBseSgqLCBzaW1wbGlmeSA9IEZBTFNFLCBVU0UuTkFNRVMgPSBGQUxTRSlgIOWSjGBsYXBwbHkoKilg55qE6L+U5Zue5YC85piv55u45ZCM55qE44CC5aaC5p6c5Y+C5pWwc2ltcGxpZnk9VFJVRe+8jOWImeWHveaVsHNhcHBseeeahOi/lOWbnuWAvOS4jeaYr+S4gOS4qmxpc3TvvIzogIzmmK/kuIDkuKrnn6npmLXvvJvoi6VzaW1wbGlmeT1GQUxTRe+8jOWImeWHveaVsHNhcHBseeeahOi/lOWbnuWAvOS7jeeEtuaYr+S4gOS4qmxpc3TjgIJzYXBwbHnlr7nliJfooajlupTnlKjlh73mlbDvvIzov5Tlm57lkJHph4/vvIzov5nkuKrmr5TovoPluLjnlKjvvIznrYnku7fkuo51bmxpc3QobGFwcGx5KOKApikp77yM55SobGFwcGx56K6h566X77yM54S25ZCO5oqK57uT5p6c5Y+Y5Li65ZCR6YeP77ybDQpgYGB7ciB9DQpzYXBwbHkoeCwgcXVhbnRpbGUsc2ltcGxpZnk9RkFMU0UsdXNlLm5hbWVzPUZBTFNFKQ0KIyAkYQ0KIyAgICAwJSAgIDI1JSAgIDUwJSAgIDc1JSAgMTAwJSANCiMgIDEuMDAgIDMuMjUgIDUuNTAgIDcuNzUgMTAuMDAgDQojIA0KIyAkYmV0YQ0KIyAgICAgICAgICAwJSAgICAgICAgIDI1JSAgICAgICAgIDUwJSAgICAgICAgIDc1JSAgICAgICAgMTAwJSANCiMgIDAuMDQ5Nzg3MDcgIDAuMjUxNjA3MzYgIDEuMDAwMDAwMDAgIDUuMDUzNjY4OTYgMjAuMDg1NTM2OTIgDQojIA0KIyAkbG9naWMNCiMgICAwJSAgMjUlICA1MCUgIDc1JSAxMDAlIA0KIyAgMC4wICAwLjAgIDAuNSAgMS4wICAxLjAgDQoNCiPlj4LmlbBzaW1wbGlmeT1UUlVF55qE5oOF5Ya1LHNhcHBseSh4LCBxdWFudGlsZSwgc2ltcGxpZnkgPSAxKSwNCiMgZGVmYXVsdDogc2FwcGx5KFgsIEZVTiwgLi4uLCBzaW1wbGlmeSA9IFRSVUUsIFVTRS5OQU1FUyA9IFRSVUUpDQpzYXBwbHkoeCwgcXVhbnRpbGUpDQojICAgICAgICAgIGEgICAgICAgIGJldGEgbG9naWMNCiMgMCUgICAgMS4wMCAgMC4wNDk3ODcwNyAgIDAuMA0KIyAyNSUgICAzLjI1ICAwLjI1MTYwNzM2ICAgMC4wDQojIDUwJSAgIDUuNTAgIDEuMDAwMDAwMDAgICAwLjUNCiMgNzUlICAgNy43NSAgNS4wNTM2Njg5NiAgIDEuMA0KIyAxMDAlIDEwLjAwIDIwLjA4NTUzNjkyICAgMS4wDQoNCj9zYXBwbHkNCmBgYA0KDQojIyDlh73mlbBtYXBwbHkNCg0K5Ye95pWwbWFwcGx55piv5Ye95pWwc2FwcGx555qE5Y+Y5b2i54mI77yMbWFwcGx5IOWwhuWHveaVsCBGVU4g5L6d5qyh5bqU55So5q+P5LiA5Liq5Y+C5pWw55qE56ys5LiA5Liq5YWD57Sg44CB56ys5LqM5Liq5YWD57Sg44CB56ys5LiJ5Liq5YWD57Sg5LiK44CCDQoNCuWHveaVsG1hcHBseeeahOS9v+eUqOagvOW8j+WmguS4i++8mmBtYXBwbHkoRlVOLCAuLi4sIE1vcmVBcmdzID0gTlVMTCwgU0lNUExJRlkgPSBUUlVFLFVTRS5OQU1FUyA9IFRSVUUpYOWFtuS4reWPguaVsE1vcmVBcmdz6KGo56S65Ye95pWwRlVO55qE5Y+C5pWw5YiX6KGo44CCDQoNCuekuuS+i+S7o+egge+8mg0KYGBge3J9DQojIyB0aW1lcyBhcmd1bWVudCBpcyBmb3IgcmVwKCkNCm1hcHBseShyZXAsIHRpbWVzPTE6NCwgeD00OjEpDQojIFtbMV1dDQojIFsxXSA0DQojIA0KIyBbWzJdXQ0KIyBbMV0gMyAzDQojIA0KIyBbWzNdXQ0KIyBbMV0gMiAyIDINCiMgDQojIFtbNF1dDQojIFsxXSAxIDEgMSAxDQoNCiPnm7TmjqXkvb/nlKjlh73mlbByZXDnmoTnu5PmnpzvvJoNCnJlcCgxOjQsMTo0KQ0KIyBbMV0gMSAyIDIgMyAzIDMgNCA0IDQgNA0KYGBgDQoNCiMjIGFnZ3JlYXRlDQoNCmFnZ3JlYXRl77yac3BsaXQtYXBwbHktY29tYmluZSzmi4bliIbmiJDlrZDpm4bvvIzliIbliKvorqHnrpflkIjlubbnu5PmnpzovpPlh7rvvJsNCg0K